#3757 Allow subfolders in "My Outfits"

master
Andrey Kleshchev 2025-04-08 20:28:50 +03:00 committed by Andrey Kleshchev
parent a7f3785cd6
commit b95b20a1be
9 changed files with 134 additions and 6 deletions

View File

@ -122,7 +122,7 @@ void LLAppearanceListener::getOutfitsList(LLSD const &data)
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
LLIsType is_category(LLAssetType::AT_CATEGORY);
LLIsFolderType is_category(LLFolderType::FT_OUTFIT);
gInventory.collectDescendentsIf(outfits_id, cat_array, item_array, LLInventoryModel::EXCLUDE_TRASH, is_category);
response["outfits"] = llsd::toMap(cat_array,

View File

@ -2916,6 +2916,10 @@ bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
// create a new folder and populate it with links to original objects
dropToMyOutfits(inv_cat, cb);
}
else if (getCategory() && getCategory()->getParentUUID() == my_outifts_id)
{
dropToMyOutfitsSubfolder(inv_cat, mUUID, cb);
}
// if target is current outfit folder we use link
else if (move_is_into_current_outfit &&
(inv_cat->getPreferredType() == LLFolderType::FT_NONE ||
@ -4016,7 +4020,14 @@ void LLFolderBridge::perform_pasteFromClipboard()
U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit");
if (cat && can_move_to_my_outfits(model, cat, max_items_to_wear))
{
dropToMyOutfits(cat, cb);
if (mUUID == my_outifts_id)
{
dropToMyOutfits(cat, cb);
}
else if (getCategory() && getCategory()->getParentUUID() == mUUID)
{
dropToMyOutfitsSubfolder(cat, mUUID, cb);
}
}
else
{
@ -4256,6 +4267,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
if (outfits_id == mUUID)
{
items.push_back(std::string("New Outfit Folder"));
items.push_back(std::string("New Outfit"));
}
@ -5331,13 +5343,36 @@ void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLI
// Note: creation will take time, so passing folder id to callback is slightly unreliable,
// but so is collecting and passing descendants' ids
inventory_func_type func = boost::bind(outfitFolderCreatedCallback, inv_cat->getUUID(), _1, cb, mInventoryPanel);
gInventory.createNewCategory(dest_id,
getInventoryModel()->createNewCategory(dest_id,
LLFolderType::FT_OUTFIT,
inv_cat->getName(),
func,
inv_cat->getThumbnailUUID());
}
void LLFolderBridge::dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest_id, LLPointer<LLInventoryCallback> cb)
{
LLViewerInventoryCategory* cat = getInventoryModel()->getCategory(dest_id);
const LLUUID outfits_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
inventory_func_type func = boost::bind(outfitFolderCreatedCallback, inv_cat->getUUID(), _1, cb, mInventoryPanel);
if (cat && cat->getParentUUID() == outfits_id)
{
getInventoryModel()->createNewCategory(dest_id,
LLFolderType::FT_OUTFIT,
inv_cat->getName(),
func,
inv_cat->getThumbnailUUID());
}
else
{
getInventoryModel()->createNewCategory(outfits_id,
LLFolderType::FT_OUTFIT,
inv_cat->getName(),
func,
inv_cat->getThumbnailUUID());
}
}
void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id,
LLUUID cat_dest_id,
LLPointer<LLInventoryCallback> cb,
@ -5441,6 +5476,10 @@ bool LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
const bool move_is_into_favorites = (mUUID == favorites_id);
const bool move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id);
const bool move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
const bool move_is_into_my_outfits_subfolder = move_is_into_my_outfits
&& getCategory()
&& getCategory()->getParentUUID() == my_outifts_id
&& getCategory()->getPreferredType() != LLFolderType::FT_OUTFIT;
const bool move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id);
const bool move_is_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id);
const bool move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id);
@ -5511,7 +5550,7 @@ bool LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
}
else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit))
{
accept = can_move_to_outfit(inv_item, move_is_into_current_outfit);
accept = !move_is_into_my_outfits_subfolder && can_move_to_outfit(inv_item, move_is_into_current_outfit);
}
else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks))
{

View File

@ -369,6 +369,7 @@ protected:
void dropToFavorites(LLInventoryItem* inv_item, LLPointer<LLInventoryCallback> cb = NULL);
void dropToOutfit(LLInventoryItem* inv_item, bool move_is_into_current_outfit, LLPointer<LLInventoryCallback> cb = NULL);
void dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer<LLInventoryCallback> cb = NULL);
void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest, LLPointer<LLInventoryCallback> cb = NULL);
//--------------------------------------------------------------------
// Messy hacks for handling folder options

View File

@ -2621,6 +2621,11 @@ bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(const LLInventoryIte
return false;
}
bool LLIsFolderType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
{
return cat && cat->getPreferredType() == mType;
}
bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
{
if(mType == LLAssetType::AT_CATEGORY)

View File

@ -234,6 +234,24 @@ protected:
// the type is the type passed in during construction.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LLIsFolderType : public LLInventoryCollectFunctor
{
public:
LLIsFolderType(LLFolderType::EType type) : mType(type) {}
virtual ~LLIsFolderType() {}
virtual bool operator()(LLInventoryCategory* cat,
LLInventoryItem* item);
protected:
LLFolderType::EType mType;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLIsType
//
// Implementation of a LLInventoryCollectFunctor which returns true if
// the type is the type passed in during construction.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LLIsType : public LLInventoryCollectFunctor
{
public:

View File

@ -64,6 +64,7 @@ const S32 FAST_LOAD_THUMBNAIL_TRSHOLD = 50; // load folders below this value imm
bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, bool drop, std::string& tooltip_msg, bool is_link);
bool dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, bool drop, std::string& tooltip_msg, bool user_confirm);
void dropToMyOutfits(LLInventoryCategory* inv_cat);
void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID& dest_id);
class LLGalleryPanel: public LLPanel
{
@ -3898,6 +3899,10 @@ bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat,
// create a new folder and populate it with links to original objects
dropToMyOutfits(inv_cat);
}
else if (dest_cat && dest_cat->getParentUUID() == my_outifts_id)
{
dropToMyOutfitsSubfolder(inv_cat, dest_id);
}
// if target is current outfit folder we use link
else if (move_is_into_current_outfit &&
(inv_cat->getPreferredType() == LLFolderType::FT_NONE ||
@ -4041,3 +4046,11 @@ void dropToMyOutfits(LLInventoryCategory* inv_cat)
inventory_func_type func = boost::bind(&outfitFolderCreatedCallback, inv_cat->getUUID(), _1);
gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), func, inv_cat->getThumbnailUUID());
}
void dropToMyOutfitsSubfolder(LLInventoryCategory* inv_cat, const LLUUID &dest_id)
{
// Note: creation will take time, so passing folder id to callback is slightly unreliable,
// but so is collecting and passing descendants' ids
inventory_func_type func = boost::bind(&outfitFolderCreatedCallback, inv_cat->getUUID(), _1);
gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), func, inv_cat->getThumbnailUUID());
}

View File

@ -771,6 +771,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men
if(is_outfits && !isRootFolder())
{
items.push_back(std::string("New Outfit Folder"));
items.push_back(std::string("New Outfit"));
}

View File

@ -819,6 +819,49 @@ void LLOutfitListBase::observerCallback(const LLUUID& category_id)
refreshList(category_id);
}
class LLIsOutfitListFolder : public LLInventoryCollectFunctor
{
public:
LLIsOutfitListFolder()
{
mOutfitsId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
}
virtual ~LLIsOutfitListFolder() {}
bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) override
{
if (cat)
{
if (cat->getPreferredType() == LLFolderType::FT_OUTFIT)
{
return true;
}
if (cat->getPreferredType() == LLFolderType::FT_NONE
&& cat->getParentUUID() == mOutfitsId)
{
LLViewerInventoryCategory* inv_cat = dynamic_cast<LLViewerInventoryCategory*>(cat);
if (inv_cat && inv_cat->getDescendentCount() > 3)
{
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cats, items);
if (items->size() > 3) // eyes, skin, hair and shape are required
{
// For now assume this to be an old style outfit, not a subfolder
// but ideally no such 'outfits' should be left in My Outfits
// Todo: stop counting FT_NONE as outfits,
// convert obvious outfits into FT_OUTFIT
return true;
}
}
}
}
return false;
}
protected:
LLUUID mOutfitsId;
};
void LLOutfitListBase::refreshList(const LLUUID& category_id)
{
bool wasNull = mRefreshListState.CategoryUUID.isNull();
@ -828,13 +871,13 @@ void LLOutfitListBase::refreshList(const LLUUID& category_id)
LLInventoryModel::item_array_t item_array;
// Collect all sub-categories of a given category.
LLIsType is_category(LLAssetType::AT_CATEGORY);
LLIsOutfitListFolder is_outfit;
gInventory.collectDescendentsIf(
category_id,
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH,
is_category);
is_outfit);
// Memorize item names for each UUID
std::map<LLUUID, std::string> names;

View File

@ -151,6 +151,14 @@
function="Inventory.DoCreate"
parameter="category" />
</menu_item_call>
<menu_item_call
label="New Folder"
layout="topleft"
name="New Outfit Folder">
<menu_item_call.on_click
function="Inventory.DoCreate"
parameter="category" />
</menu_item_call>
<menu_item_call
label="New Outfit"
layout="topleft"