diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 10f7927202..adb99f7ba5 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -215,6 +215,26 @@ BOOL LLInventoryObject::importLegacyStream(std::istream& input_stream) { mType = LLAssetType::lookup(valuestr); } + else if (0 == strcmp("metadata", keyword)) + { + LLSD metadata(valuestr); + if (metadata.has("thumbnail")) + { + const LLSD& thumbnail = metadata["thumbnail"]; + if (thumbnail.has("asset_id")) + { + setThumbnailUUID(thumbnail["asset_id"].asUUID()); + } + else + { + setThumbnailUUID(LLUUID::null); + } + } + else + { + setThumbnailUUID(LLUUID::null); + } + } else if(0 == strcmp("name", keyword)) { //strcpy(valuestr, buffer + strlen(keyword) + 3); diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 62be0c28e8..735e2d529e 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -188,12 +188,12 @@ void LLScrollbar::setPageSize( S32 page_size ) } } -BOOL LLScrollbar::isAtBeginning() +bool LLScrollbar::isAtBeginning() const { return mDocPos == 0; } -BOOL LLScrollbar::isAtEnd() +bool LLScrollbar::isAtEnd() const { return mDocPos == getDocPosMax(); } diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 5f2f490d81..9be9d22db8 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -105,8 +105,8 @@ public: bool setDocPos( S32 pos, BOOL update_thumb = TRUE ); S32 getDocPos() const { return mDocPos; } - BOOL isAtBeginning(); - BOOL isAtEnd(); + bool isAtBeginning() const; + bool isAtEnd() const; // Setting both at once. void setDocParams( S32 size, S32 pos ); diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index f19d02120f..613bb2ca2f 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -99,8 +99,10 @@ public: void pageDown(S32 overlap = 0); void goToTop(); void goToBottom(); - bool isAtTop() { return mScrollbar[VERTICAL]->isAtBeginning(); } - bool isAtBottom() { return mScrollbar[VERTICAL]->isAtEnd(); } + bool isAtTop() const { return mScrollbar[VERTICAL]->isAtBeginning(); } + bool isAtBottom() const { return mScrollbar[VERTICAL]->isAtEnd(); } + S32 getDocPosVertical() const { return mScrollbar[VERTICAL]->getDocPos(); } + S32 getDocPosHorizontal() const { return mScrollbar[HORIZONTAL]->getDocPos(); } S32 getBorderWidth() const; // Scrollbar accessor LLScrollbar* getScrollbar(SCROLL_ORIENTATION orientation) { return mScrollbar[orientation]; } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c5b5ca5467..a4cacf136f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -356,6 +356,7 @@ set(viewer_SOURCE_FILES llfloatermyscripts.cpp llfloatermyenvironment.cpp llfloaternamedesc.cpp + llfloaternewfeaturenotification.cpp llfloaternotificationsconsole.cpp llfloaternotificationstabbed.cpp llfloaterobjectweights.cpp @@ -1145,6 +1146,7 @@ set(viewer_HEADER_FILES llfloatermyscripts.h llfloatermyenvironment.h llfloaternamedesc.h + llfloaternewfeaturenotification.h llfloaternotificationsconsole.h llfloaternotificationstabbed.h llfloaterobjectweights.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2146a03cdf..26708096db 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7835,6 +7835,17 @@ Backup 0 + LastUIFeatureVersion + + Comment + UI Feature Version number for tracking feature notification between viewer builds + Persist + 1 + Type + S32 + Value + 0 + LastFindPanel Comment diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 2d820e226d..3bd236a3b0 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -139,6 +139,8 @@ const F64 CHAT_AGE_FAST_RATE = 3.0; const F32 MIN_FIDGET_TIME = 8.f; // seconds const F32 MAX_FIDGET_TIME = 20.f; // seconds +const S32 UI_FEATURE_VERSION = 1; + // The agent instance. LLAgent gAgent; @@ -408,7 +410,7 @@ LLAgent::LLAgent() : mHideGroupTitle(FALSE), mGroupID(), - mInitialized(FALSE), + mInitialized(false), mListener(), mDoubleTapRunTimer(), @@ -497,7 +499,7 @@ LLAgent::LLAgent() : mNextFidgetTime(0.f), mCurrentFidget(0), - mFirstLogin(FALSE), + mFirstLogin(false), mOutfitChosen(FALSE), mVoiceConnected(false), @@ -590,7 +592,7 @@ void LLAgent::init() mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_AGENT); - mInitialized = TRUE; + mInitialized = true; } //----------------------------------------------------------------------------- @@ -645,6 +647,36 @@ void LLAgent::onAppFocusGained() // } } +void LLAgent::setFirstLogin(bool b) +{ + mFirstLogin = b; + + if (mFirstLogin) + { + // Don't notify new users about new features + S32 feature_version = gSavedSettings.getS32("LastUIFeatureVersion"); + if (feature_version < UI_FEATURE_VERSION) + { + gSavedSettings.setS32("LastUIFeatureVersion", UI_FEATURE_VERSION); + } + } +} + +void LLAgent::showLatestFeatureNotification() +{ + // Notify user about new thumbnail support + S32 feature_version = gSavedSettings.getS32("LastUIFeatureVersion"); + if (feature_version < UI_FEATURE_VERSION) + { + // Need to open on top even if called from onOpen, + // do on idle to make sure it's on top + doOnIdleOneTime([]() + { + LLFloaterReg::showInstance("new_feature_notification"); + }); + gSavedSettings.setS32("LastUIFeatureVersion", UI_FEATURE_VERSION); + } +} void LLAgent::ageChat() { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 27c73e32dc..767be5a081 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -120,15 +120,17 @@ private: //-------------------------------------------------------------------- public: void onAppFocusGained(); - void setFirstLogin(BOOL b) { mFirstLogin = b; } + void setFirstLogin(bool b); // Return TRUE if the database reported this login as the first for this particular user. - BOOL isFirstLogin() const { return mFirstLogin; } - BOOL isInitialized() const { return mInitialized; } + bool isFirstLogin() const { return mFirstLogin; } + bool isInitialized() const { return mInitialized; } + + void showLatestFeatureNotification(); public: std::string mMOTD; // Message of the day private: - BOOL mInitialized; - BOOL mFirstLogin; + bool mInitialized; + bool mFirstLogin; boost::shared_ptr mListener; //-------------------------------------------------------------------- diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp index 58161b92e9..f18629762c 100644 --- a/indra/newview/llfloaterchangeitemthumbnail.cpp +++ b/indra/newview/llfloaterchangeitemthumbnail.cpp @@ -711,7 +711,9 @@ void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id) texture_floaterp->setLocalTextureEnabled(FALSE); texture_floaterp->setBakeTextureEnabled(FALSE); texture_floaterp->setCanApplyImmediately(false); - texture_floaterp->setCanApply(false, true); + texture_floaterp->setCanApply(false, true, false /*Hide 'preview disabled'*/); + texture_floaterp->setDimentionsLimits(LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX, + LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN); addDependentFloater(texture_floaterp); } diff --git a/indra/newview/llfloaternewfeaturenotification.cpp b/indra/newview/llfloaternewfeaturenotification.cpp new file mode 100644 index 0000000000..7c3fe8cdc8 --- /dev/null +++ b/indra/newview/llfloaternewfeaturenotification.cpp @@ -0,0 +1,68 @@ +/** + * @file llfloaternewfeaturenotification.cpp + * @brief LLFloaterNewFeatureNotification class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaternewfeaturenotification.h" + + +LLFloaterNewFeatureNotification::LLFloaterNewFeatureNotification(const LLSD& key) + : LLFloater(key) +{ +} + +LLFloaterNewFeatureNotification::~LLFloaterNewFeatureNotification() +{ +} + +BOOL LLFloaterNewFeatureNotification::postBuild() +{ + setCanDrag(FALSE); + getChild("close_btn")->setCommitCallback(boost::bind(&LLFloaterNewFeatureNotification::onCloseBtn, this)); + return TRUE; +} + +void LLFloaterNewFeatureNotification::onOpen(const LLSD& key) +{ + centerOnScreen(); +} + +void LLFloaterNewFeatureNotification::onCloseBtn() +{ + closeFloater(); +} + +void LLFloaterNewFeatureNotification::centerOnScreen() +{ + LLVector2 window_size = LLUI::getInstance()->getWindowSize(); + centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY]))); + LLFloaterView* parent = dynamic_cast(getParent()); + if (parent) + { + parent->bringToFront(this); + } +} + diff --git a/indra/newview/llfloaternewfeaturenotification.h b/indra/newview/llfloaternewfeaturenotification.h new file mode 100644 index 0000000000..95501451dc --- /dev/null +++ b/indra/newview/llfloaternewfeaturenotification.h @@ -0,0 +1,49 @@ +/** + * @file llfloaternewfeaturenotification.h + * @brief LLFloaterNewFeatureNotification class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_FLOATER_NEW_FEATURE_NOTOFICATION_H +#define LL_FLOATER_NEW_FEATURE_NOTOFICATION_H + +#include "llfloater.h" + +class LLFloaterNewFeatureNotification: + public LLFloater +{ + friend class LLFloaterReg; +public: + BOOL postBuild() override; + void onOpen(const LLSD& key) override; + +private: + LLFloaterNewFeatureNotification(const LLSD& key); + /*virtual*/ ~LLFloaterNewFeatureNotification(); + + void centerOnScreen(); + + void onCloseBtn(); +}; + +#endif diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 1855297661..e7b5537e96 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -1649,11 +1649,15 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol callback_dest_create); }); } - if (depth == 1) + else if (depth == 1) { // We need a version folder gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName(), callback_dest_create); } + else + { + callback_dest_create(dest_folder); + } } else { @@ -1952,6 +1956,7 @@ void validate_marketplacelistings( { // Create a new folder const LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); + const LLUUID origin_uuid = viewer_cat->getUUID(); LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); std::string folder_name = (depth >= 1 ? viewer_cat->getName() : viewer_inv_item->getName()); LLFolderType::EType new_folder_type = (items_vector_it->first == default_key ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); @@ -1975,7 +1980,7 @@ void validate_marketplacelistings( parent_uuid, new_folder_type, folder_name, - [uuid_vector, cb_result, cb_msg, depth, parent_uuid, notify_observers](const LLUUID &new_category_id) + [uuid_vector, cb_result, cb_msg, depth, parent_uuid, origin_uuid, notify_observers](const LLUUID &new_category_id) { // Move each item to the new folder std::vector::const_reverse_iterator iter = uuid_vector.rbegin(); @@ -1996,6 +2001,31 @@ void validate_marketplacelistings( iter++; } + if (origin_uuid != parent_uuid) + { + // We might have moved last item from a folder, check if it needs to be removed + LLViewerInventoryCategory* cat = gInventory.getCategory(origin_uuid); + if (cat->getDescendentCount() == 0) + { + // Remove previous folder if it ends up empty + if (cb_msg) + { + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); + cb_msg(message, depth, LLError::LEVEL_WARN); + } + gInventory.removeCategory(cat->getUUID()); + if (notify_observers) + { + gInventory.notifyObservers(); + } + } + } + // Next type update_marketplace_category(parent_uuid); update_marketplace_category(new_category_id); diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index 25a009c609..025ee61f3b 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -36,6 +36,7 @@ #include "llinventoryfunctions.h" #include "llinventoryicon.h" #include "llinventorymodel.h" +#include "llinventorymodelbackgroundfetch.h" #include "llthumbnailctrl.h" #include "lltextbox.h" #include "llviewerfoldertype.h" @@ -1045,6 +1046,7 @@ BOOL LLInventoryGallery::handleKeyHere(KEY key, MASK mask) } } } + handled = TRUE; break; case KEY_DELETE: #if LL_DARWIN @@ -1056,6 +1058,7 @@ BOOL LLInventoryGallery::handleKeyHere(KEY key, MASK mask) { deleteSelection(); } + handled = TRUE; break; case KEY_F2: @@ -1251,12 +1254,26 @@ void LLInventoryGallery::onFocusReceived() // inventory now handles cut/copy/paste/delete gEditMenuHandler = this; - LLPanel::onFocusReceived(); - + // Tab support, when tabbing into this view, select first item if (mSelectedItemID.notNull() && mItemMap[mSelectedItemID]) { - mItemMap[mSelectedItemID]->setSelected(true); + LLInventoryGalleryItem* focus_item = mItemMap[mSelectedItemID]; + focus_item->setSelected(true); + focus_item->setFocus(TRUE); } + else if (mIndexToItemMap.size() > 0 && mItemToSelect.isNull()) + { + // choose any items from visible rect + S32 vert_offset = mScrollPanel->getDocPosVertical(); + S32 panel_size = mVerticalGap + mRowPanelHeight; + S32 n = llclamp((S32)(vert_offset / panel_size) * mItemsInRow, 0, (S32)(mIndexToItemMap.size() - 1) ); + + LLInventoryGalleryItem* focus_item = mIndexToItemMap[n]; + changeItemSelection(focus_item->getUUID(), true); + focus_item->setFocus(TRUE); + } + + LLPanel::onFocusReceived(); } void LLInventoryGallery::showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id) @@ -2244,6 +2261,15 @@ void LLInventoryGalleryItem::setSelected(bool value) { mSelected = value; mTextBgPanel->setBackgroundVisible(value); + + if(mSelected) + { + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + if(item && !item->isFinished()) + { + LLInventoryModelBackgroundFetch::instance().start(mUUID, false); + } + } } BOOL LLInventoryGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask) diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 4e84c7c796..aac392ef32 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -1550,6 +1550,45 @@ void LLInventoryPanel::onFocusReceived() // inventory now handles cut/copy/paste/delete LLEditMenuHandler::gEditMenuHandler = mFolderRoot.get(); + // Tab support, when tabbing into this view, select first item + // (ideally needs to account for scroll) + bool select_first = mSelectThisID.isNull() && mFolderRoot.get() && mFolderRoot.get()->getSelectedCount() == 0; + + if (select_first) + { + LLFolderViewFolder::folders_t::const_iterator folders_it = mFolderRoot.get()->getFoldersBegin(); + LLFolderViewFolder::folders_t::const_iterator folders_end = mFolderRoot.get()->getFoldersEnd(); + + for (; folders_it != folders_end; ++folders_it) + { + const LLFolderViewFolder* folder_view = *folders_it; + if (folder_view->getVisible()) + { + const LLFolderViewModelItemInventory* modelp = static_cast(folder_view->getViewModelItem()); + setSelectionByID(modelp->getUUID(), TRUE); + select_first = false; + break; + } + } + } + + if (select_first) + { + LLFolderViewFolder::items_t::const_iterator items_it = mFolderRoot.get()->getItemsBegin(); + LLFolderViewFolder::items_t::const_iterator items_end = mFolderRoot.get()->getItemsEnd(); + + for (; items_it != items_end; ++items_it) + { + const LLFolderViewItem* item_view = *items_it; + if (item_view->getVisible()) + { + const LLFolderViewModelItemInventory* modelp = static_cast(item_view->getViewModelItem()); + setSelectionByID(modelp->getUUID(), TRUE); + break; + } + } + } + LLPanel::onFocusReceived(); } 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 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 r("outfit_gallery_i LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p) : LLPanel(p), - mTexturep(NULL), + mGallery(nullptr), + mTexturep(nullptr), mSelected(false), mWorn(false), mDefaultImage(true), @@ -763,9 +1032,64 @@ 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("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& 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 outfit_map_t; typedef outfit_map_t::value_type outfit_map_value_t; outfit_map_t mOutfitMap; - typedef std::map item_num_map_t; + typedef std::map item_num_map_t; typedef item_num_map_t::value_type item_numb_map_value_t; item_num_map_t mItemIndexMap; + std::map 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 mTexturep; LLUUID mUUID; LLUUID mImageAssetId; diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index f4c2fe10bd..08afbf57a4 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -3024,20 +3024,11 @@ void LLPanelMainInventory::onCombinationRootChanged(bool gallery_clicked) void LLPanelMainInventory::onCombinationGallerySelectionChanged(const LLUUID& category_id) { - if(category_id != LLUUID::null) - { - mCombinationInventoryPanel->unSelectAll(); - } } void LLPanelMainInventory::onCombinationInventorySelectionChanged(const std::deque& items, BOOL user_action) { onSelectionChange(mCombinationInventoryPanel, items, user_action); - - if(!items.empty()) - { - mCombinationGalleryPanel->clearSelection(); - } } void LLPanelMainInventory::updatePanelVisibility() diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index fb6b5f1348..adc181f20a 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -2440,7 +2440,7 @@ void LLPanelProfileSecondLife::onShowTexturePicker() }); texture_floaterp->setLocalTextureEnabled(FALSE); texture_floaterp->setBakeTextureEnabled(FALSE); - texture_floaterp->setCanApply(false, true); + texture_floaterp->setCanApply(false, true, false); parent_floater->addDependentFloater(mFloaterTexturePickerHandle); @@ -2847,7 +2847,7 @@ void LLPanelProfileFirstLife::onChangePhoto() } }); texture_floaterp->setLocalTextureEnabled(FALSE); - texture_floaterp->setCanApply(false, true); + texture_floaterp->setCanApply(false, true, false); parent_floater->addDependentFloater(mFloaterTexturePickerHandle); diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 5aa7135151..b016d3cabe 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -477,6 +477,8 @@ void LLSidepanelInventory::onOpen(const LLSD& key) } #endif + gAgent.showLatestFeatureNotification(); + if(key.size() == 0) { // set focus on filter editor when side tray inventory shows up diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 2316970fa3..a1cf0777c0 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -4645,7 +4645,7 @@ bool process_login_success_response(U32 &first_sim_size_x, U32 &first_sim_size_y std::string flag = login_flags["ever_logged_in"]; if(!flag.empty()) { - gAgent.setFirstLogin((flag == "N") ? TRUE : FALSE); + gAgent.setFirstLogin(flag == "N"); } /* Flag is currently ignored by the viewer. @@ -4767,7 +4767,7 @@ bool process_login_success_response(U32 &first_sim_size_x, U32 &first_sim_size_y std::string fake_initial_outfit_name = gSavedSettings.getString("FakeInitialOutfitName"); if (!fake_initial_outfit_name.empty()) { - gAgent.setFirstLogin(TRUE); + gAgent.setFirstLogin(true); sInitialOutfit = fake_initial_outfit_name; if (sInitialOutfitGender.empty()) { diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 05bb7468d8..4cb997297a 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -178,6 +178,8 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( mSelectedItemPinned( FALSE ), mCanApply(true), mCanPreview(true), + mMaxDim(S32_MAX), + mMinDim(0), mPreviewSettingChanged(false), mOnFloaterCommitCallback(NULL), mOnFloaterCloseCallback(NULL), @@ -318,19 +320,36 @@ void LLFloaterTexturePicker::stopUsingPipette() } } -void LLFloaterTexturePicker::updateImageStats() +bool LLFloaterTexturePicker::updateImageStats() { + bool result = true; if (mTexturep.notNull()) { //RN: have we received header data for this image? - if (mTexturep->getFullWidth() > 0 && mTexturep->getFullHeight() > 0) + S32 width = mTexturep->getFullWidth(); + S32 height = mTexturep->getFullHeight(); + if (width > 0 && height > 0) { - std::string formatted_dims = llformat("%d x %d", mTexturep->getFullWidth(),mTexturep->getFullHeight()); - mResolutionLabel->setTextArg("[DIMENSIONS]", formatted_dims); - if (mOnUpdateImageStatsCallback) - { - mOnUpdateImageStatsCallback(mTexturep); - } + if (width < mMinDim + || width > mMaxDim + || height < mMinDim + || height > mMaxDim + ) + { + std::string formatted_dims = llformat("%dx%d", width, height); + mResolutionWarning->setTextArg("[TEXDIM]", formatted_dims); + result = false; + } + else + { + std::string formatted_dims = llformat("%d x %d", width, height); + mResolutionLabel->setTextArg("[DIMENSIONS]", formatted_dims); + } + + if (mOnUpdateImageStatsCallback) + { + mOnUpdateImageStatsCallback(mTexturep); + } } else { @@ -341,6 +360,23 @@ void LLFloaterTexturePicker::updateImageStats() { mResolutionLabel->setTextArg("[DIMENSIONS]", std::string("")); } + mResolutionLabel->setVisible(result); + mResolutionWarning->setVisible(!result); + + // Hide buttons and pipete to make space for mResolutionWarning + // Hiding buttons is suboptimal, but at the moment limited to inventory thumbnails + // may be this should be an info/warning icon with a tooltip? + S32 index = mModeSelector->getValue().asInteger(); + if (index == 0) + { + mDefaultBtn->setVisible(result); + mNoneBtn->setVisible(result); + mBlankBtn->setVisible(result); + mPipetteBtn->setVisible(result); + // Special additions + mTransparentBtn->setVisible(result); + } + return result; } // virtual @@ -471,13 +507,29 @@ BOOL LLFloaterTexturePicker::postBuild() mTentativeLabel = getChild("Multiple"); mResolutionLabel = getChild("size_lbl"); + mResolutionWarning = getChild("over_limit_lbl"); - childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this); - childSetAction("None", LLFloaterTexturePicker::onBtnNone,this); - childSetAction("Blank", LLFloaterTexturePicker::onBtnBlank,this); - childSetAction("Transparent", LLFloaterTexturePicker::onBtnTransparent,this); // FIRE-5082: "Transparent" button in Texture Panel + mDefaultBtn = getChild("Default"); + mNoneBtn = getChild("None"); + mBlankBtn = getChild("Blank"); + mPipetteBtn = getChild("Pipette"); + mSelectBtn = getChild("Select"); + mCancelBtn = getChild("Cancel"); + // Special additions + mTransparentBtn = getChild("Transparent"); + mUUIDBtn = getChild("TextureKeyApply"); + mUUIDEditor = getChild("TextureKey"); + mDefaultBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnSetToDefault,this)); + mNoneBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnNone, this)); + mBlankBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnBlank, this)); + mPipetteBtn->setCommitCallback(boost::bind(&LLFloaterTexturePicker::onBtnPipette, this)); + mSelectBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnSelect, this)); + mCancelBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnCancel, this)); + // Special additions + mTransparentBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnTransparent, this)); + mUUIDBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnApplyTexture, this)); childSetCommitCallback("show_folders_check", onShowFolders, this); getChildView("show_folders_check")->setVisible( FALSE); @@ -548,13 +600,6 @@ BOOL LLFloaterTexturePicker::postBuild() getChildView("show_folders_check")->setEnabled(FALSE); } - getChild("Pipette")->setCommitCallback( boost::bind(&LLFloaterTexturePicker::onBtnPipette, this)); - // UUID picker - childSetAction("TextureKeyApply", LLFloaterTexturePicker::onBtnApplyTexture,this); - // - childSetAction("Cancel", LLFloaterTexturePicker::onBtnCancel,this); - childSetAction("Select", LLFloaterTexturePicker::onBtnSelect,this); - // update permission filter once UI is fully initialized updateFilterPermMask(); mSavedFolderState.setApply(FALSE); @@ -574,13 +619,13 @@ void LLFloaterTexturePicker::draw() static LLCachedControl max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); drawConeToOwner(mContextConeOpacity, max_opacity, mOwner); - updateImageStats(); + bool valid_dims = updateImageStats(); // if we're inactive, gray out "apply immediate" checkbox getChildView("show_folders_check")->setEnabled(mActive && mCanApplyImmediately && !mNoCopyTextureSelected); - getChildView("Select")->setEnabled(mActive && mCanApply); - getChildView("Pipette")->setEnabled(mActive); - getChild("Pipette")->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); + mSelectBtn->setEnabled(mActive && mCanApply && valid_dims); + mPipetteBtn->setEnabled(mActive); + mPipetteBtn->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); //BOOL allow_copy = FALSE; if( mOwner ) @@ -614,11 +659,10 @@ void LLFloaterTexturePicker::draw() mTentativeLabel->setVisible( FALSE ); } - getChildView("Default")->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative); - getChildView("Blank")->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative); - getChildView("Transparent")->setEnabled(mImageAssetID != mTransparentImageAssetID || mTentative); // FIRE-5082: "Transparent" button in Texture Panel - getChildView("None")->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative)); - + mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative); + mBlankBtn->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative); + mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative)); + mTransparentBtn->setEnabled(mImageAssetID != mTransparentImageAssetID || mTentative); // FIRE-5082: "Transparent" button in Texture Panel LLFloater::draw(); if( isMinimized() ) @@ -949,11 +993,11 @@ void LLFloaterTexturePicker::onModeSelect(LLUICtrl* ctrl, void *userdata) //int index = self->mModeSelector->getValue().asInteger(); int index = self->mModeSelector->getSelectedIndex(); - self->getChild("Default")->setVisible(index == 0 ? TRUE : FALSE); - self->getChild("Blank")->setVisible(index == 0 ? TRUE : FALSE); - self->getChild("Transparent")->setVisible(index == 0 ? TRUE : FALSE); // FIRE-5082: "Transparent" button in Texture Panel - self->getChild("None")->setVisible(index == 0 ? TRUE : FALSE); - self->getChild("Pipette")->setVisible(index == 0 ? TRUE : FALSE); + self->mDefaultBtn->setVisible(index == 0 ? TRUE : FALSE); + self->mBlankBtn->setVisible(index == 0 ? TRUE : FALSE); + self->mNoneBtn->setVisible(index == 0 ? TRUE : FALSE); + self->mPipetteBtn->setVisible(index == 0 ? TRUE : FALSE); + self->mTransparentBtn->setVisible(index == 0 ? TRUE : FALSE); // FIRE-5082: "Transparent" button in Texture Panel self->getChild("inventory search editor")->setVisible(index == 0 ? TRUE : FALSE); self->getChild("inventory panel")->setVisible(index == 0 ? TRUE : FALSE); @@ -1243,10 +1287,10 @@ void LLFloaterTexturePicker::updateFilterPermMask() //mInventoryPanel->setFilterPermMask( getFilterPermMask() ); Commented out due to no-copy texture loss. } -void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply) +void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply, bool inworld_image) { - getChildRef("Select").setEnabled(can_apply); - getChildRef("preview_disabled").setVisible(!can_preview); + mSelectBtn->setEnabled(can_apply); + getChildRef("preview_disabled").setVisible(!can_preview && inworld_image); getChildRef("apply_immediate_check").setVisible(can_preview); mCanApply = can_apply; @@ -1254,6 +1298,17 @@ void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply) mPreviewSettingChanged = true; } +void LLFloaterTexturePicker::setDimentionsLimits(S32 max_dim, S32 min_dim) +{ + mMaxDim = max_dim; + mMinDim = min_dim; + + std::string formatted_dims = llformat("%dx%d", mMinDim, mMinDim); + mResolutionWarning->setTextArg("[MINTEXDIM]", formatted_dims); + formatted_dims = llformat("%dx%d", mMaxDim, mMaxDim); + mResolutionWarning->setTextArg("[MAXTEXDIM]", formatted_dims); +} + void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string ) { std::string upper_case_search_string = search_string; diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index c4334f4ce9..2074d33de2 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -308,7 +308,7 @@ public: // New functions void setImageID(const LLUUID& image_asset_id, bool set_selection = true); - void updateImageStats(); + bool updateImageStats(); // true if within limits const LLUUID& getAssetID() { return mImageAssetID; } const LLUUID& findItemID(const LLUUID& asset_id, BOOL copyable_only, BOOL ignore_library = FALSE); void setCanApplyImmediately(BOOL b); @@ -326,7 +326,8 @@ public: void onFilterEdit(const std::string& search_string); - void setCanApply(bool can_preview, bool can_apply); + void setCanApply(bool can_preview, bool can_apply, bool inworld_image = true); + void setDimentionsLimits(S32 max_dim, S32 min_dim); void setTextureSelectedCallback(const texture_selected_callback& cb) { mTextureSelectedCallback = cb; } void setOnFloaterCloseCallback(const floater_close_callback& cb) { mOnFloaterCloseCallback = cb; } void setOnFloaterCommitCallback(const floater_commit_callback& cb) { mOnFloaterCommitCallback = cb; } @@ -383,6 +384,7 @@ protected: LLTextBox* mTentativeLabel; LLTextBox* mResolutionLabel; + LLTextBox* mResolutionWarning; std::string mPendingName; BOOL mActive; @@ -402,11 +404,23 @@ protected: //LLComboBox* mModeSelector; LLRadioGroup* mModeSelector; LLScrollListCtrl* mLocalScrollCtrl; + LLButton* mDefaultBtn; + LLButton* mNoneBtn; + LLButton* mBlankBtn; + LLButton* mPipetteBtn; + LLButton* mSelectBtn; + LLButton* mCancelBtn; + // Special additions + LLLineEditor* mUUIDEditor{ nullptr }; + LLButton* mUUIDBtn{ nullptr }; + LLButton* mTransparentBtn{ nullptr }; private: bool mCanApply; bool mCanPreview; bool mPreviewSettingChanged; + S32 mMaxDim; + S32 mMinDim; texture_selected_callback mTextureSelectedCallback; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 30a37109e1..ee3873ad98 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -100,6 +100,7 @@ #include "llfloatermyscripts.h" #include "llfloatermyenvironment.h" #include "llfloaternamedesc.h" +#include "llfloaternewfeaturenotification.h" #include "llfloaternotificationsconsole.h" #include "llfloaternotificationstabbed.h" #include "llfloaterobjectweights.h" @@ -284,6 +285,7 @@ public: "avatar_picker", "camera", "camera_presets", + "change_item_thumbnail" "classified", "add_landmark", "delete_pref_preset", @@ -302,6 +304,7 @@ public: "message_critical", // Modal!!! Login specific. If this is in use elsewhere, better to create a non modal variant "message_tos", // Modal!!! Login specific. "mute_object_by_name", + "new_feature_notification", "publish_classified", "save_pref_preset", "save_camera_preset", @@ -476,6 +479,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("moveview", "floater_moveview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("mute_object_by_name", "floater_mute_object.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("mini_map", "floater_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("new_feature_notification", "floater_new_feature_notification.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("notifications_console", "floater_notifications_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/skins/default/xui/de/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/de/floater_texture_ctrl.xml index 3e0e9fc184..b2e1e0f24a 100644 --- a/indra/newview/skins/default/xui/de/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/de/floater_texture_ctrl.xml @@ -18,6 +18,9 @@ Größe: [DIMENSIONS] + + Ausgewählte Textur ist [TEXDIM]. Inventar-Abbildung muss quadratisch und zwischen [MINTEXDIM] und [MAXTEXDIM] sein. +