diff options
author | Igor Borovkov <iborovkov@productengine.com> | 2010-04-23 14:35:24 +0300 |
---|---|---|
committer | Igor Borovkov <iborovkov@productengine.com> | 2010-04-23 14:35:24 +0300 |
commit | 181315db32165d71f2e1baec66caca24cd74ced7 (patch) | |
tree | 5765ee786ca43ef69413eeb44e81552981d5c246 /indra | |
parent | 40df6a5c868876e935428399db8fe325e11b5178 (diff) |
completed EXT-6721 (Enable UI for user modification of wearable order)
- added functionality to change order of wearables
- added ad-hoc up and down buttons on a button bar ("up" means closer to the body) - https://jira.secondlife.com/secure/attachment/38464/screenshot-1.jpg
- added displaying wearables as sorted by order on the Edit Outfit panel (top list)
Reviewed by Neal Orman at https://codereview.productengine.com/secondlife/r/280/
--HG--
branch : product-engine
Diffstat (limited to 'indra')
-rw-r--r-- | indra/newview/llagentwearables.cpp | 33 | ||||
-rw-r--r-- | indra/newview/llagentwearables.h | 2 | ||||
-rw-r--r-- | indra/newview/llappearancemgr.cpp | 93 | ||||
-rw-r--r-- | indra/newview/llappearancemgr.h | 2 | ||||
-rw-r--r-- | indra/newview/llpaneloutfitedit.cpp | 36 | ||||
-rw-r--r-- | indra/newview/llpaneloutfitedit.h | 4 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_outfit_edit.xml | 24 |
7 files changed, 189 insertions, 5 deletions
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 466f2d499d..8a880e5ace 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -2031,6 +2031,39 @@ void LLAgentWearables::animateAllWearableParams(F32 delta, BOOL upload_bake) } } +bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool closer_to_body) +{ + if (!item) return false; + if (!item->isWearableType()) return false; + + wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(item->getWearableType()); + if (wearable_iter == mWearableDatas.end()) return false; + + wearableentry_vec_t& wearable_vec = wearable_iter->second; + if (wearable_vec.empty()) return false; + + const LLUUID& asset_id = item->getAssetUUID(); + + //nowhere to move if the wearable is already on any boundary (closest to the body/furthest from the body) + if (closer_to_body && asset_id == wearable_vec.front()->getAssetID()) return false; + if (!closer_to_body && asset_id == wearable_vec.back()->getAssetID()) return false; + + for (U32 i = 0; i < wearable_vec.size(); ++i) + { + LLWearable* wearable = wearable_vec[i]; + if (!wearable) continue; + if (wearable->getAssetID() != asset_id) continue; + + //swapping wearables + U32 swap_i = closer_to_body ? i-1 : i+1; + wearable_vec[i] = wearable_vec[swap_i]; + wearable_vec[swap_i] = wearable; + return true; + } + + return false; +} + void LLAgentWearables::updateServer() { sendAgentWearablesUpdate(); diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 585fd3f8b3..d3b18f68f1 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -82,6 +82,8 @@ public: void animateAllWearableParams(F32 delta, BOOL upload_bake); + bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); + //-------------------------------------------------------------------- // Accessors //-------------------------------------------------------------------- diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 9e02886e4f..a6a3aa28d6 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -122,6 +122,38 @@ private: bool mAppend; }; + +//Inventory callback updating "dirty" state when destroyed +class LLUpdateDirtyState: public LLInventoryCallback +{ +public: + LLUpdateDirtyState() {} + virtual ~LLUpdateDirtyState(){ LLAppearanceMgr::getInstance()->updateIsDirty(); } + virtual void fire(const LLUUID&) {} +}; + + +//Inventory collect functor collecting wearables of a specific wearable type +class LLFindClothesOfType : public LLInventoryCollectFunctor +{ +public: + LLFindClothesOfType(EWearableType type) : mWearableType(type) {} + virtual ~LLFindClothesOfType() {} + virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if (!item) return false; + if (item->getType() != LLAssetType::AT_CLOTHING) return false; + + LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(item); + if (!vitem || vitem->getWearableType() != mWearableType) return false; + + return true; + } + + const EWearableType mWearableType; +}; + + LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(): mFireCount(0) { @@ -1593,8 +1625,11 @@ bool LLAppearanceMgr::updateBaseOutfit() // in a Base Outfit we do not remove items, only links purgeCategory(base_outfit_id, false); + + LLPointer<LLInventoryCallback> dirty_state_updater = new LLUpdateDirtyState(); + //COF contains only links so we copy to the Base Outfit only links - shallowCopyCategoryContents(getCOF(), base_outfit_id, NULL); + shallowCopyCategoryContents(getCOF(), base_outfit_id, dirty_state_updater); return true; } @@ -1802,6 +1837,62 @@ void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove) } } + +bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body) +{ + if (!item || !item->isWearableType()) return false; + if (item->getType() != LLAssetType::AT_CLOTHING) return false; + if (!gInventory.isObjectDescendentOf(item->getUUID(), getCOF())) return false; + + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendentsIf(getCOF(), cats, items, true, LLFindClothesOfType(item->getWearableType())); + if (items.empty()) return false; + + //*TODO all items are not guarantied to have valid descriptions (check?) + std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType())); + + if (closer_to_body && items.front() == item) return false; + if (!closer_to_body && items.back() == item) return false; + + LLInventoryModel::item_array_t::iterator it = std::find(items.begin(), items.end(), item); + if (items.end() == it) return false; + + + //swapping descriptions + closer_to_body ? --it : ++it; + LLViewerInventoryItem* swap_item = *it; + if (!swap_item) return false; + std::string tmp = swap_item->LLInventoryItem::getDescription(); + swap_item->setDescription(item->LLInventoryItem::getDescription()); + item->setDescription(tmp); + + + //items need to be updated on a dataserver + item->setComplete(TRUE); + item->updateServer(FALSE); + gInventory.updateItem(item); + + swap_item->setComplete(TRUE); + swap_item->updateServer(FALSE); + gInventory.updateItem(swap_item); + + //to cause appearance of the agent to be updated + bool result = false; + if (result = gAgentWearables.moveWearable(item, closer_to_body)) + { + gAgentAvatarp->wearableUpdated(item->getWearableType(), TRUE); + } + + setOutfitDirty(true); + + //*TODO do we need to notify observers here in such a way? + gInventory.notifyObservers(); + + return result; +} + + //#define DUMP_CAT_VERBOSE void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg) diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index efb5274c5b..a308a3efa9 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -141,6 +141,8 @@ public: LLUUID makeNewOutfitLinks(const std::string& new_folder_name); + bool moveWearable(LLViewerInventoryItem* item, bool closer_to_body); + protected: LLAppearanceMgr(); ~LLAppearanceMgr(); diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index ce17e1d624..ef6161de85 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -71,6 +71,9 @@ const U64 WEARABLE_MASK = (1LL << LLInventoryType::IT_WEARABLE); const U64 ATTACHMENT_MASK = (1LL << LLInventoryType::IT_ATTACHMENT) | (1LL << LLInventoryType::IT_OBJECT); const U64 ALL_ITEMS_MASK = WEARABLE_MASK | ATTACHMENT_MASK; +static const std::string SAVE_BTN("save_btn"); +static const std::string REVERT_BTN("revert_btn"); + class LLInventoryLookObserver : public LLInventoryObserver { public: @@ -218,10 +221,9 @@ BOOL LLPanelOutfitEdit::postBuild() mEditWearableBtn->setVisible(FALSE); mEditWearableBtn->setCommitCallback(boost::bind(&LLPanelOutfitEdit::onEditWearableClicked, this)); - childSetAction("revert_btn", boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance())); + childSetAction(REVERT_BTN, boost::bind(&LLAppearanceMgr::wearBaseOutfit, LLAppearanceMgr::getInstance())); - childSetAction("save_btn", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false)); - childSetAction("save_as_btn", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true)); + childSetAction(SAVE_BTN, boost::bind(&LLPanelOutfitEdit::saveOutfit, this, false)); childSetAction("save_flyout_btn", boost::bind(&LLPanelOutfitEdit::showSaveMenu, this)); LLUICtrl::CommitCallbackRegistry::ScopedRegistrar save_registar; @@ -229,9 +231,21 @@ BOOL LLPanelOutfitEdit::postBuild() save_registar.add("Outfit.SaveAsNew.Action", boost::bind(&LLPanelOutfitEdit::saveOutfit, this, true)); mSaveMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_save_outfit.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + childSetAction("move_closer_btn", boost::bind(&LLPanelOutfitEdit::moveWearable, this, true)); + childSetAction("move_further_btn", boost::bind(&LLPanelOutfitEdit::moveWearable, this, false)); + return TRUE; } +void LLPanelOutfitEdit::moveWearable(bool closer_to_body) +{ + LLViewerInventoryItem* wearable_to_move = gInventory.getItem(mLookContents->getSelectionInterface()->getCurrentID()); + LLAppearanceMgr::getInstance()->moveWearable(wearable_to_move, closer_to_body); + + //*TODO why not to listen to inventory? + updateLookInfo(); +} + void LLPanelOutfitEdit::showAddWearablesPanel() { childSetVisible("add_wearables_panel", childGetValue("add_btn")); @@ -256,6 +270,8 @@ void LLPanelOutfitEdit::saveOutfit(bool as_new) { panel_outfits_inventory->onSave(); } + + //*TODO how to get to know when base outfit is updated or new outfit is created? } void LLPanelOutfitEdit::showSaveMenu() @@ -530,10 +546,12 @@ void LLPanelOutfitEdit::lookFetched(void) columns[0]["value"] = item->getName(); columns[1]["column"] = "look_item_sort"; columns[1]["type"] = "text"; // TODO: multi-wearable sort "type" should go here. - columns[1]["value"] = "BAR"; // TODO: Multi-wearable sort index should go here + columns[1]["value"] = item->LLInventoryItem::getDescription(); mLookContents->addElement(row); } + + updateVerbs(); } void LLPanelOutfitEdit::updateLookInfo() @@ -577,4 +595,14 @@ void LLPanelOutfitEdit::displayCurrentOutfit() updateLookInfo(); } +//private +void LLPanelOutfitEdit::updateVerbs() +{ + bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty(); + + childSetEnabled(SAVE_BTN, outfit_is_dirty); + childSetEnabled(REVERT_BTN, outfit_is_dirty); + + mSaveMenu->setItemEnabled("save_outfit", outfit_is_dirty); +} diff --git a/indra/newview/llpaneloutfitedit.h b/indra/newview/llpaneloutfitedit.h index 69e8016534..36f107b453 100644 --- a/indra/newview/llpaneloutfitedit.h +++ b/indra/newview/llpaneloutfitedit.h @@ -86,6 +86,8 @@ public: // Sends a request for data about the given parcel, which will // only update the location if there is none already available. + void moveWearable(bool closer_to_body); + void showAddWearablesPanel(); void showWearablesFilter(); void saveOutfit(bool as_new = false); @@ -108,6 +110,8 @@ public: private: + void updateVerbs(); + //*TODO got rid of mCurrentOutfitID LLUUID mCurrentOutfitID; diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml index c77e4e8d5e..79b952237e 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml @@ -222,6 +222,30 @@ top="1" width="31" /> <button + follows="bottom|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Movement_Forward_On" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="1" + name="move_closer_btn" + top="1" + width="31" /> + <button + follows="bottom|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Movement_Backward_On" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="1" + name="move_further_btn" + top="1" + width="31" /> + <button follows="bottom|right" height="25" image_hover_unselected="Toolbar_Middle_Over" |