diff options
author | Andrey Kleshchev <andreykproductengine@lindenlab.com> | 2023-06-27 00:24:13 +0300 |
---|---|---|
committer | Andrey Kleshchev <andreykproductengine@lindenlab.com> | 2023-06-27 00:29:34 +0300 |
commit | cd0c40c71af767dd66694dc3329e08823aa6b6e7 (patch) | |
tree | 2cd1b5d91968fb20581d3ecc5124a45a249482a5 | |
parent | 434f2ee512dc6edf4a6dfbafd7265d121038bf0b (diff) |
SL-19904 Outfit gallery keyboard support
-rw-r--r-- | indra/newview/lloutfitgallery.cpp | 353 | ||||
-rw-r--r-- | indra/newview/lloutfitgallery.h | 31 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_outfit_gallery.xml | 42 |
3 files changed, 356 insertions, 70 deletions
diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 415d0a96d7..de988555c5 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -146,6 +146,264 @@ void LLOutfitGallery::draw() } } +BOOL LLOutfitGallery::handleKeyHere(KEY key, MASK mask) +{ + BOOL handled = FALSE; + switch (key) + { + case KEY_RETURN: + // Open selected items if enter key hit on the inventory panel + if (mask == MASK_NONE && mSelectedOutfitUUID.notNull()) + { + // Or should it wearSelectedOutfit? + getSelectedItem()->openOutfitsContent(); + } + handled = TRUE; + break; + case KEY_DELETE: +#if LL_DARWIN + case KEY_BACKSPACE: +#endif + // Delete selected items if delete or backspace key hit on the inventory panel + // Note: on Mac laptop keyboards, backspace and delete are one and the same + if (mSelectedOutfitUUID.notNull()) + { + onRemoveOutfit(mSelectedOutfitUUID); + } + handled = TRUE; + break; + + case KEY_F2: + LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID); + handled = TRUE; + break; + + case KEY_PAGE_UP: + if (mScrollPanel) + { + mScrollPanel->pageUp(30); + } + handled = TRUE; + break; + + case KEY_PAGE_DOWN: + if (mScrollPanel) + { + mScrollPanel->pageDown(30); + } + handled = TRUE; + break; + + case KEY_HOME: + if (mScrollPanel) + { + mScrollPanel->goToTop(); + } + handled = TRUE; + break; + + case KEY_END: + if (mScrollPanel) + { + mScrollPanel->goToBottom(); + } + handled = TRUE; + break; + + case KEY_LEFT: + moveLeft(); + handled = TRUE; + break; + + case KEY_RIGHT: + moveRight(); + handled = TRUE; + break; + + case KEY_UP: + moveUp(); + handled = TRUE; + break; + + case KEY_DOWN: + moveDown(); + handled = TRUE; + break; + + default: + break; + } + + if (handled) + { + mOutfitGalleryMenu->hide(); + } + + return handled; +} + +void LLOutfitGallery::moveUp() +{ + if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + S32 n = mItemIndexMap[item]; + n -= mItemsInRow; + if (n >= 0) + { + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + ChangeOutfitSelection(nullptr, item_id); + item->setFocus(TRUE); + + scrollToShowItem(mSelectedOutfitUUID); + } + } + } +} + +void LLOutfitGallery::moveDown() +{ + if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + S32 n = mItemIndexMap[item]; + n += mItemsInRow; + if (n < mItemsAddedCount) + { + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + ChangeOutfitSelection(nullptr, item_id); + item->setFocus(TRUE); + + scrollToShowItem(mSelectedOutfitUUID); + } + } + } +} + +void LLOutfitGallery::moveLeft() +{ + if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + // Might be better to get item from panel + S32 n = mItemIndexMap[item]; + n--; + if (n < 0) + { + n = mItemsAddedCount - 1; + } + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + ChangeOutfitSelection(nullptr, item_id); + item->setFocus(TRUE); + + scrollToShowItem(mSelectedOutfitUUID); + } + } +} + +void LLOutfitGallery::moveRight() +{ + if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + S32 n = mItemIndexMap[item]; + n++; + if (n == mItemsAddedCount) + { + n = 0; + } + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + ChangeOutfitSelection(nullptr, item_id); + item->setFocus(TRUE); + + scrollToShowItem(mSelectedOutfitUUID); + } + } +} + +void LLOutfitGallery::onFocusLost() +{ + LLOutfitListBase::onFocusLost(); + + if (mSelectedOutfitUUID.notNull()) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + item->setSelected(false); + } + } +} + +void LLOutfitGallery::onFocusReceived() +{ + LLOutfitListBase::onFocusReceived(); + + if (mSelectedOutfitUUID.notNull()) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + item->setSelected(true); + } + } +} + +void LLOutfitGallery::onRemoveOutfit(const LLUUID& outfit_cat_id) +{ + LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(onOutfitsRemovalConfirmation, _1, _2, outfit_cat_id)); +} + +void LLOutfitGallery::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + + if (outfit_cat_id.notNull()) + { + gInventory.removeCategory(outfit_cat_id); + } +} + +void LLOutfitGallery::scrollToShowItem(const LLUUID& item_id) +{ + LLOutfitGalleryItem* item = mOutfitMap[item_id]; + if (item) + { + const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect(); + + LLRect item_rect; + item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel); + LLRect overlap_rect(item_rect); + overlap_rect.intersectWith(visible_content_rect); + + //Scroll when the selected item is outside the visible area + if (overlap_rect.getHeight() + 5 < item->getRect().getHeight()) + { + LLRect content_rect = mScrollPanel->getContentWindowRect(); + LLRect constraint_rect; + constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + + LLRect item_doc_rect; + item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel); + + mScrollPanel->scrollToShowRect(item_doc_rect, constraint_rect); + } + } +} + void LLOutfitGallery::updateRowsIfNeeded() { if(((getRect().getWidth() - mRowPanelWidth) > mItemWidth) && mRowCount > 1) @@ -266,8 +524,9 @@ void LLOutfitGallery::addToGallery(LLOutfitGalleryItem* item) mHiddenItems.push_back(item); return; } + mItemIndexMap[item] = mItemsAddedCount; + mIndexToItemMap[mItemsAddedCount] = item; mItemsAddedCount++; - mItemIndexMap[item] = mItemsAddedCount - 1; int n = mItemsAddedCount; int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; int n_prev = n - 1; @@ -303,6 +562,7 @@ void LLOutfitGallery::removeFromGalleryLast(LLOutfitGalleryItem* item) int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1; mItemsAddedCount--; + mIndexToItemMap.erase(mItemsAddedCount); bool remove_row = row_count != row_count_prev; removeFromLastRow(mItems[mItemsAddedCount]); @@ -328,6 +588,7 @@ void LLOutfitGallery::removeFromGalleryMiddle(LLOutfitGalleryItem* item) } int n = mItemIndexMap[item]; mItemIndexMap.erase(item); + mIndexToItemMap.erase(n); std::vector<LLOutfitGalleryItem*> saved; for (int i = mItemsAddedCount - 1; i > n; i--) { @@ -361,9 +622,15 @@ LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID gitem->setFollowsTop(); gitem->setOutfitName(name); gitem->setUUID(outfit_id); + gitem->setGallery(this); return gitem; } +LLOutfitGalleryItem* LLOutfitGallery::getSelectedItem() +{ + return mOutfitMap[mSelectedOutfitUUID]; +} + void LLOutfitGallery::buildGalleryPanel(int row_count) { LLPanel::Params params; @@ -608,6 +875,7 @@ void LLOutfitGallery::onChangeOutfitSelection(LLWearableItemsList* list, const L { mOutfitMap[category_id]->setSelected(TRUE); } + // mSelectedOutfitUUID will be set in LLOutfitListBase::ChangeOutfitSelection } void LLOutfitGallery::wearSelectedOutfit() @@ -659,7 +927,8 @@ static LLDefaultChildRegistry::Register<LLOutfitGalleryItem> r("outfit_gallery_i LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p) : LLPanel(p), - mTexturep(NULL), + mGallery(nullptr), + mTexturep(nullptr), mSelected(false), mWorn(false), mDefaultImage(true), @@ -764,8 +1033,63 @@ BOOL LLOutfitGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask) BOOL LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask) { + return openOutfitsContent() || LLPanel::handleDoubleClick(x, y, mask); +} + +BOOL LLOutfitGalleryItem::handleKeyHere(KEY key, MASK mask) +{ + if (!mGallery) + { + return FALSE; + } + + BOOL handled = FALSE; + switch (key) + { + case KEY_LEFT: + mGallery->moveLeft(); + handled = true; + break; + + case KEY_RIGHT: + mGallery->moveRight(); + handled = true; + break; + + case KEY_UP: + mGallery->moveUp(); + handled = true; + break; + + case KEY_DOWN: + mGallery->moveDown(); + handled = true; + break; + + default: + break; + } + return handled; +} + +void LLOutfitGalleryItem::onFocusLost() +{ + setSelected(false); + + LLPanel::onFocusLost(); +} + +void LLOutfitGalleryItem::onFocusReceived() +{ + setSelected(true); + + LLPanel::onFocusReceived(); +} + +bool LLOutfitGalleryItem::openOutfitsContent() +{ LLTabContainer* appearence_tabs = LLPanelOutfitsInventory::findInstance()->getChild<LLTabContainer>("appearance_tabs"); - if (appearence_tabs && (mUUID != LLUUID())) + if (appearence_tabs && mUUID.notNull()) { appearence_tabs->selectTabByName("outfitslist_tab"); LLPanel* panel = appearence_tabs->getCurrentPanel(); @@ -778,12 +1102,11 @@ BOOL LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask) outfit_list->setSelectedOutfitByUUID(mUUID); LLAccordionCtrlTab* tab = accordion->getSelectedTab(); tab->showAndFocusHeader(); - return TRUE; + return true; } } } - - return LLPanel::handleDoubleClick(x, y, mask); + return false; } bool LLOutfitGalleryItem::setImageAssetId(LLUUID image_asset_id) @@ -829,7 +1152,7 @@ LLContextMenu* LLOutfitGalleryContextMenu::createMenu() boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); registrar.add("Outfit.Edit", boost::bind(editOutfit)); registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); - registrar.add("Outfit.Delete", boost::bind(&LLOutfitGalleryContextMenu::onRemoveOutfit, this, selected_id)); + registrar.add("Outfit.Delete", boost::bind(LLOutfitGallery::onRemoveOutfit, selected_id)); registrar.add("Outfit.Create", boost::bind(&LLOutfitGalleryContextMenu::onCreate, this, _2)); registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitGalleryContextMenu::onThumbnail, this, selected_id)); enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitGalleryContextMenu::onEnable, this, _2)); @@ -848,22 +1171,6 @@ void LLOutfitGalleryContextMenu::onThumbnail(const LLUUID& outfit_cat_id) } } -void LLOutfitGalleryContextMenu::onRemoveOutfit(const LLUUID& outfit_cat_id) -{ - LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitGalleryContextMenu::onOutfitsRemovalConfirmation, this, _1, _2, outfit_cat_id)); -} - -void LLOutfitGalleryContextMenu::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; // canceled - - if (outfit_cat_id.notNull()) - { - gInventory.removeCategory(outfit_cat_id); - } -} - void LLOutfitGalleryContextMenu::onCreate(const LLSD& data) { LLWearableType::EType type = LLWearableType::getInstance()->typeNameToType(data.asString()); diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h index 16116d4b71..9915752962 100644 --- a/indra/newview/lloutfitgallery.h +++ b/indra/newview/lloutfitgallery.h @@ -74,6 +74,18 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& info); /*virtual*/ void draw(); + /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); + void moveUp(); + void moveDown(); + void moveLeft(); + void moveRight(); + + /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); + + static void onRemoveOutfit(const LLUUID& outfit_cat_id); + static void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id); + void scrollToShowItem(const LLUUID& item_id); void wearSelectedOutfit(); @@ -108,8 +120,6 @@ protected: void applyFilter(LLOutfitGalleryItem* item, const std::string& filter_substring); private: - void uploadPhoto(LLUUID outfit_id); - void uploadOutfitImage(const std::vector<std::string>& filenames, LLUUID outfit_id); LLUUID getPhotoAssetId(const LLUUID& outfit_id); LLUUID getDefaultPhoto(); void addToGallery(LLOutfitGalleryItem* item); @@ -127,6 +137,7 @@ private: void updateGalleryWidth(); LLOutfitGalleryItem* buildGalleryItem(std::string name, LLUUID outfit_id); + LLOutfitGalleryItem* getSelectedItem(); void onTextureSelectionChanged(LLInventoryItem* itemp); @@ -170,9 +181,10 @@ private: typedef std::map<LLUUID, LLOutfitGalleryItem*> outfit_map_t; typedef outfit_map_t::value_type outfit_map_value_t; outfit_map_t mOutfitMap; - typedef std::map<LLOutfitGalleryItem*, int> item_num_map_t; + typedef std::map<LLOutfitGalleryItem*, S32> item_num_map_t; typedef item_num_map_t::value_type item_numb_map_value_t; item_num_map_t mItemIndexMap; + std::map<S32, LLOutfitGalleryItem*> mIndexToItemMap; LLInventoryCategoriesObserver* mOutfitsObserver; @@ -185,14 +197,13 @@ public: LLOutfitGalleryContextMenu(LLOutfitListBase* outfit_list) : LLOutfitContextMenu(outfit_list), mOutfitList(outfit_list){} + protected: /* virtual */ LLContextMenu* createMenu(); bool onEnable(LLSD::String param); bool onVisible(LLSD::String param); void onThumbnail(const LLUUID& outfit_cat_id); void onCreate(const LLSD& data); - void onRemoveOutfit(const LLUUID& outfit_cat_id); - void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id); private: LLOutfitListBase* mOutfitList; }; @@ -226,14 +237,21 @@ public: /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); + /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); + + bool openOutfitsContent(); + void setGallery(LLOutfitGallery* gallery) { mGallery = gallery; } void setDefaultImage(); bool setImageAssetId(LLUUID asset_id); LLUUID getImageAssetId(); void setOutfitName(std::string name); void setOutfitWorn(bool value); void setSelected(bool value); - void setUUID(LLUUID outfit_id) {mUUID = outfit_id;} + void setUUID(const LLUUID &outfit_id) {mUUID = outfit_id;} + LLUUID getUUID() const { return mUUID; } std::string getItemName() {return mOutfitName;} bool isDefaultImage() {return mDefaultImage;} @@ -242,6 +260,7 @@ public: void setHidden(bool hidden) {mHidden = hidden;} private: + LLOutfitGallery* mGallery; LLPointer<LLViewerFetchedTexture> mTexturep; LLUUID mUUID; LLUUID mImageAssetId; diff --git a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml index e3790ae09b..e951d25391 100644 --- a/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml +++ b/indra/newview/skins/default/xui/en/panel_outfit_gallery.xml @@ -40,50 +40,10 @@ layout="topleft" left="4" top="0" + tab_stop="true" name="gallery_scroll_panel" opaque="false" top_pad="0"> - <!--outfit_gallery_item - layout="topleft" - left="10" - name="preview_outfit1" - height="175" - width="150" - follows="left|top"/--> - <!--layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="0" name="top_gallery_stack" orientation="horizontal"> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> - </layout_panel> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> - </layout_panel> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> - </layout_panel> - </layout_stack> - <layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="190" name="top_gallery_stack" orientation="horizontal"> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> - </layout_panel> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> - </layout_panel> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> - </layout_panel> - </layout_stack> - <layout_stack follows="left|right" height="180" width="498" layout="topleft" left="0" animate="false" top="380" name="top_gallery_stack" orientation="horizontal"> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top_gallery_panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit1" height="175" width="150" follows="left|top"/> - </layout_panel> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> - </layout_panel> - <layout_panel follows="left|top" height="175" width="166" layout="topleft" left="0" top="0" auto_resize="false" visible="true" name="top panel"> - <outfit_gallery_item layout="topleft" left="10" name="preview_outfit2" height="175" width="150" follows="left|top"/> - </layout_panel> - </layout_stack--> - <!--</panel>--> </scroll_container> <panel background_visible="true" |