From 7c100d54dda03db62a8d2c8be6a5e02ea9280f9b Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 29 Jun 2023 02:33:58 +0300
Subject: [PATCH] SL-19928 Make thumbnail selection failures more user friendly

---
 .../newview/llfloaterchangeitemthumbnail.cpp  |   4 +-
 indra/newview/llpanelprofile.cpp              |   4 +-
 indra/newview/lltexturectrl.cpp               | 110 +++++++++++++-----
 indra/newview/lltexturectrl.h                 |  14 ++-
 .../default/xui/en/floater_texture_ctrl.xml   |  16 +++
 5 files changed, 114 insertions(+), 34 deletions(-)

diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp
index 48378cb724e..1ad235ac0a7 100644
--- a/indra/newview/llfloaterchangeitemthumbnail.cpp
+++ b/indra/newview/llfloaterchangeitemthumbnail.cpp
@@ -710,7 +710,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/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 078c1370ee3..a49effc3427 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -1923,7 +1923,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);
 
@@ -2263,7 +2263,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/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 6f29d45f6b5..ec0ae395734 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -176,6 +176,8 @@ LLFloaterTexturePicker::LLFloaterTexturePicker(
 	mSelectedItemPinned( FALSE ),
 	mCanApply(true),
 	mCanPreview(true),
+    mMaxDim(S32_MAX),
+    mMinDim(0),
 	mPreviewSettingChanged(false),
 	mOnFloaterCommitCallback(NULL),
 	mOnFloaterCloseCallback(NULL),
@@ -270,19 +272,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("%d x %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
 		{
@@ -293,6 +312,21 @@ 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);
+    }
+    return result;
 }
 
 // virtual
@@ -410,11 +444,22 @@ BOOL LLFloaterTexturePicker::postBuild()
 	mTentativeLabel = getChild<LLTextBox>("Multiple");
 
 	mResolutionLabel = getChild<LLTextBox>("size_lbl");
+    mResolutionWarning = getChild<LLTextBox>("over_limit_lbl");
 
 
-	childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this);
-	childSetAction("None", LLFloaterTexturePicker::onBtnNone,this);
-	childSetAction("Blank", LLFloaterTexturePicker::onBtnBlank,this);
+    mDefaultBtn = getChild<LLButton>("Default");
+    mNoneBtn = getChild<LLButton>("None");
+    mBlankBtn = getChild<LLButton>("Blank");
+    mPipetteBtn = getChild<LLButton>("Pipette");
+    mSelectBtn = getChild<LLButton>("Select");
+    mCancelBtn = getChild<LLButton>("Cancel");
+
+    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));
 
 
 	childSetCommitCallback("show_folders_check", onShowFolders, this);
@@ -481,10 +526,6 @@ BOOL LLFloaterTexturePicker::postBuild()
 		getChildView("show_folders_check")->setEnabled(FALSE);
 	}
 
-	getChild<LLUICtrl>("Pipette")->setCommitCallback( boost::bind(&LLFloaterTexturePicker::onBtnPipette, this));
-	childSetAction("Cancel", LLFloaterTexturePicker::onBtnCancel,this);
-	childSetAction("Select", LLFloaterTexturePicker::onBtnSelect,this);
-
 	// update permission filter once UI is fully initialized
 	updateFilterPermMask();
 	mSavedFolderState.setApply(FALSE);
@@ -504,13 +545,13 @@ void LLFloaterTexturePicker::draw()
     static LLCachedControl<F32> 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<LLUICtrl>("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 ) 
@@ -544,9 +585,9 @@ void LLFloaterTexturePicker::draw()
 			mTentativeLabel->setVisible( FALSE  );
 		}
 
-		getChildView("Default")->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative);
-		getChildView("Blank")->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative);
-		getChildView("None")->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative));
+		mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative);
+		mBlankBtn->setEnabled(mImageAssetID != mBlankImageAssetID || mTentative);
+		mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative));
 
 		LLFloater::draw();
 
@@ -820,10 +861,10 @@ void LLFloaterTexturePicker::onModeSelect(LLUICtrl* ctrl, void *userdata)
 	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
     int index = self->mModeSelector->getValue().asInteger();
 
-	self->getChild<LLButton>("Default")->setVisible(index == 0 ? TRUE : FALSE);
-	self->getChild<LLButton>("Blank")->setVisible(index == 0 ? TRUE : FALSE);
-	self->getChild<LLButton>("None")->setVisible(index == 0 ? TRUE : FALSE);
-	self->getChild<LLButton>("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->getChild<LLFilterEditor>("inventory search editor")->setVisible(index == 0 ? TRUE : FALSE);
 	self->getChild<LLInventoryPanel>("inventory panel")->setVisible(index == 0 ? TRUE : FALSE);
 
@@ -1098,10 +1139,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<LLUICtrl>("Select").setEnabled(can_apply);
-	getChildRef<LLUICtrl>("preview_disabled").setVisible(!can_preview);
+	mSelectBtn->setEnabled(can_apply);
+	getChildRef<LLUICtrl>("preview_disabled").setVisible(!can_preview && inworld_image);
 	getChildRef<LLUICtrl>("apply_immediate_check").setVisible(can_preview);
 
 	mCanApply = can_apply;
@@ -1109,6 +1150,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("%d x %d", mMinDim, mMinDim);
+    mResolutionWarning->setTextArg("[MINTEXDIM]", formatted_dims);
+    formatted_dims = llformat("%d x %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 fbb38c4464f..8ef6d9bee7d 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -295,7 +295,7 @@ class LLFloaterTexturePicker : public LLFloater
 
 	// 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);
@@ -313,7 +313,8 @@ class LLFloaterTexturePicker : public LLFloater
 
 	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; }
@@ -364,6 +365,7 @@ class LLFloaterTexturePicker : public LLFloater
 
 	LLTextBox*			mTentativeLabel;
 	LLTextBox*			mResolutionLabel;
+    LLTextBox*          mResolutionWarning;
 
 	std::string			mPendingName;
 	BOOL				mActive;
@@ -381,11 +383,19 @@ class LLFloaterTexturePicker : public LLFloater
 
 	LLComboBox*			mModeSelector;
 	LLScrollListCtrl*	mLocalScrollCtrl;
+    LLButton*           mDefaultBtn;
+    LLButton*           mNoneBtn;
+    LLButton*           mBlankBtn;
+    LLButton*           mPipetteBtn;
+    LLButton*           mSelectBtn;
+    LLButton*           mCancelBtn;
 
 private:
 	bool mCanApply;
 	bool mCanPreview;
 	bool mPreviewSettingChanged;
+    S32 mMaxDim;
+    S32 mMinDim;
 
 
 	texture_selected_callback mTextureSelectedCallback;
diff --git a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
index 3a669113894..dc68bd0d4a2 100644
--- a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
+++ b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
@@ -83,6 +83,22 @@
      top_pad="4">
         [DIMENSIONS]
     </text>
+    <text
+     type="string"
+     text_color="Yellow"
+     length="1"
+     word_wrap="true"
+     follows="left|top"
+     height="56"
+     width="164"
+     layout="topleft"
+     left="8"
+     name="over_limit_lbl"
+     visible="false"
+     top_delta="0">
+        Selected texture is [TEXDIM], inventory image must be square, between [MINTEXDIM] and [MAXTEXDIM].
+    </text>
+    
 <!--  middle: inventory mode -->
 
     <button
-- 
GitLab