diff options
-rw-r--r-- | .github/workflows/build.yaml | 4 | ||||
-rw-r--r-- | indra/llmath/tests/v3dmath_test.cpp | 13 | ||||
-rw-r--r-- | indra/llui/llemojihelper.cpp | 11 | ||||
-rw-r--r-- | indra/llui/llemojihelper.h | 7 | ||||
-rw-r--r-- | indra/llui/llscrolllistctrl.cpp | 87 | ||||
-rw-r--r-- | indra/llui/lltexteditor.cpp | 8 | ||||
-rw-r--r-- | indra/llui/lltexteditor.h | 1 | ||||
-rw-r--r-- | indra/newview/llfloaterimsessiontab.cpp | 42 | ||||
-rw-r--r-- | indra/newview/llfloaterimsessiontab.h | 5 | ||||
-rw-r--r-- | indra/newview/llinventorybridge.cpp | 26 | ||||
-rw-r--r-- | indra/newview/llinventorygallery.cpp | 24 | ||||
-rw-r--r-- | indra/newview/llinventorygallerymenu.cpp | 35 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 16 | ||||
-rw-r--r-- | indra/test/lltut.h | 47 |
14 files changed, 229 insertions, 97 deletions
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a287f29b95..fda6545e83 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -47,7 +47,7 @@ jobs: continue-on-error: ${{ matrix.experimental }} strategy: matrix: - runner: [windows-large, macos-12-xl] + runner: [windows-large, macos-14-xlarge] configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} experimental: [false] include: @@ -73,7 +73,7 @@ jobs: # autobuild-package.xml. AUTOBUILD_VCS_INFO: "true" AUTOBUILD_VSVER: "170" - DEVELOPER_DIR: "/Applications/Xcode_14.0.1.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode_14.3.1.app/Contents/Developer" # Ensure that Linden viewer builds engage Bugsplat. BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }} build_coverity: false diff --git a/indra/llmath/tests/v3dmath_test.cpp b/indra/llmath/tests/v3dmath_test.cpp index db08419012..36ad1067a2 100644 --- a/indra/llmath/tests/v3dmath_test.cpp +++ b/indra/llmath/tests/v3dmath_test.cpp @@ -504,14 +504,11 @@ namespace tut template<> template<> void v3dmath_object::test<24>() { -#if LL_WINDOWS && _MSC_VER < 1400 - skip("This fails on VS2003!"); -#else F64 x = 10., y = 20., z = -15.; F64 angle1, angle2; LLVector3d vec3Da(x,y,z), vec3Db(x,y,z); angle1 = angle_between(vec3Da, vec3Db); - ensure("1:angle_between: Fail ", (0 == angle1)); + ensure_approximately_equals_range("1:angle_between: Fail ", angle1, 0., 1.5e-8); F64 x1 = -1., y1 = -20., z1 = -1.; vec3Da.clearVec(); vec3Da.setVec(x1,y1,z1); @@ -520,12 +517,6 @@ namespace tut vec3Da.normVec(); F64 angle = vec3Db*vec3Da; angle = acos(angle); -#if LL_WINDOWS && _MSC_VER > 1900 - skip("This fails on VS2017!"); -#else - ensure("2:angle_between: Fail ", (angle == angle2)); -#endif - -#endif + ensure_equals("2:angle_between: Fail ", angle, angle2); } } diff --git a/indra/llui/llemojihelper.cpp b/indra/llui/llemojihelper.cpp index b9441a9c91..b2c59ce775 100644 --- a/indra/llui/llemojihelper.cpp +++ b/indra/llui/llemojihelper.cpp @@ -99,6 +99,7 @@ void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, c LLFloater* pHelperFloater = LLFloaterReg::getInstance(DEFAULT_EMOJI_HELPER_FLOATER); mHelperHandle = pHelperFloater->getHandle(); mHelperCommitConn = pHelperFloater->setCommitCallback(std::bind([&](const LLSD& sdValue) { onCommitEmoji(utf8str_to_wstring(sdValue.asStringRef())[0]); }, std::placeholders::_2)); + mHelperCloseConn = pHelperFloater->setCloseCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCloseHelper(ctrl, param); }); } setHostCtrl(hostctrl_p); mEmojiCommitCb = cb; @@ -148,6 +149,16 @@ void LLEmojiHelper::onCommitEmoji(llwchar emoji) } } +void LLEmojiHelper::onCloseHelper(LLUICtrl* ctrl, const LLSD& param) +{ + mCloseSignal(ctrl, param); +} + +boost::signals2::connection LLEmojiHelper::setCloseCallback(const commit_signal_t::slot_type& cb) +{ + return mCloseSignal.connect(cb); +} + void LLEmojiHelper::setHostCtrl(LLUICtrl* hostctrl_p) { const LLUICtrl* pCurHostCtrl = mHostHandle.get(); diff --git a/indra/llui/llemojihelper.h b/indra/llui/llemojihelper.h index 2834b06061..26840eef94 100644 --- a/indra/llui/llemojihelper.h +++ b/indra/llui/llemojihelper.h @@ -51,16 +51,23 @@ public: // Eventing bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask); void onCommitEmoji(llwchar emoji); + void onCloseHelper(LLUICtrl* ctrl, const LLSD& param); + + typedef boost::signals2::signal<void(LLUICtrl* ctrl, const LLSD& param)> commit_signal_t; + boost::signals2::connection setCloseCallback(const commit_signal_t::slot_type& cb); protected: LLUICtrl* getHostCtrl() const { return mHostHandle.get(); } void setHostCtrl(LLUICtrl* hostctrl_p); private: + commit_signal_t mCloseSignal; + LLHandle<LLUICtrl> mHostHandle; LLHandle<LLFloater> mHelperHandle; boost::signals2::connection mHostCtrlFocusLostConn; boost::signals2::connection mHelperCommitConn; + boost::signals2::connection mHelperCloseConn; std::function<void(llwchar)> mEmojiCommitCb; bool mIsHideDisabled; }; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index ddd1d81cb4..8093536868 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -1105,38 +1105,32 @@ S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const void LLScrollListCtrl::selectPrevItem( bool extend_selection) { + updateSort(); + LLScrollListItem* prev_item = NULL; - if (!getFirstSelected()) - { - // select last item - selectNthItem(getItemCount() - 1); - } - else + for (LLScrollListItem* item : mItemList) { - updateSort(); - - item_list::iterator iter; - for (LLScrollListItem* cur_item : mItemList) + if (item->getSelected()) { - if (cur_item->getSelected()) - { - if (prev_item) - { - selectItem(prev_item, cur_item->getSelectedCell(), !extend_selection); - } - else - { - reportInvalidInput(); - } - break; - } + break; + } - // don't allow navigation to disabled elements - prev_item = cur_item->getEnabled() ? cur_item : prev_item; + // don't allow navigation to disabled elements + if (item->getEnabled()) + { + prev_item = item; } } + if (!prev_item) + { + reportInvalidInput(); + return; + } + + selectItem(prev_item, prev_item->getSelectedCell(), !extend_selection); + if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement)) { commitIfChanged(); @@ -1147,36 +1141,41 @@ void LLScrollListCtrl::selectPrevItem( bool extend_selection) void LLScrollListCtrl::selectNextItem( bool extend_selection) { + updateSort(); + + LLScrollListItem* current_item = NULL; LLScrollListItem* next_item = NULL; - if (!getFirstSelected()) - { - selectFirstItem(); - } - else + for (LLScrollListItem* item : mItemList) { - updateSort(); - - for (LLScrollListItem* cur_item : mItemList) + if (current_item) { - if (cur_item->getSelected()) + if (item->getEnabled()) { - if (next_item) - { - selectItem(next_item, cur_item->getSelectedCell(), !extend_selection); - } - else - { - reportInvalidInput(); - } + next_item = item; break; } - - // don't allow navigation to disabled items - next_item = cur_item->getEnabled() ? cur_item : next_item; } + else if (item->getSelected()) + { + current_item = item; + next_item = NULL; + continue; + } + else if (!next_item && item->getEnabled()) + { + next_item = item; + } + } + + if (!next_item) + { + reportInvalidInput(); + return; } + selectItem(next_item, next_item->getSelectedCell(), !extend_selection); + if (mCommitOnKeyboardMovement) { onCommit(); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index ecac800def..088fbe2744 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1211,6 +1211,14 @@ void LLTextEditor::showEmojiHelper() LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb); } +void LLTextEditor::hideEmojiHelper() +{ + if (mShowEmojiHelper) + { + LLEmojiHelper::instance().hideHelper(this); + } +} + void LLTextEditor::tryToShowEmojiHelper() { if (mReadOnly || !mShowEmojiHelper) diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index cedb79bf62..32dd95b8ac 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -207,6 +207,7 @@ public: bool getShowContextMenu() const { return mShowContextMenu; } void showEmojiHelper(); + void hideEmojiHelper(); void setShowEmojiHelper(bool show); bool getShowEmojiHelper() const { return mShowEmojiHelper; } diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index b2f2984c65..6a96dc0c69 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -39,6 +39,7 @@ #include "llchicletbar.h" #include "lldraghandle.h" #include "llemojidictionary.h" +#include "llemojihelper.h" #include "llfloaterreg.h" #include "llfloateremojipicker.h" #include "llfloaterimsession.h" @@ -298,6 +299,8 @@ bool LLFloaterIMSessionTab::postBuild() mEmojiPickerShowBtn = getChild<LLButton>("emoji_picker_show_btn"); mEmojiPickerShowBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnClicked(); }); + mEmojiPickerShowBtn->setMouseDownCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnDown(); }); + mEmojiCloseConn = LLEmojiHelper::instance().setCloseCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerClosed(); }); mGearBtn = getChild<LLButton>("gear_btn"); mAddBtn = getChild<LLButton>("add_btn"); @@ -526,8 +529,43 @@ void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked() void LLFloaterIMSessionTab::onEmojiPickerShowBtnClicked() { - mInputEditor->setFocus(true); - mInputEditor->showEmojiHelper(); + if (!mEmojiPickerShowBtn->getToggleState()) + { + mInputEditor->hideEmojiHelper(); + mInputEditor->setFocus(true); + mInputEditor->showEmojiHelper(); + mEmojiPickerShowBtn->setToggleState(true); // in case hideEmojiHelper closed a visible instance + } + else + { + mInputEditor->hideEmojiHelper(); + mEmojiPickerShowBtn->setToggleState(false); + } +} + +void LLFloaterIMSessionTab::onEmojiPickerShowBtnDown() +{ + if (mEmojiHelperLastCallbackFrame == LLFrameTimer::getFrameCount()) + { + // Helper gets closed by focus lost event on Down before before onEmojiPickerShowBtnDown + // triggers. + // If this condition is true, user pressed button and it was 'toggled' during press, + // restore 'toggled' state so that button will not reopen helper. + mEmojiPickerShowBtn->setToggleState(true); + } +} + +void LLFloaterIMSessionTab::onEmojiPickerClosed() +{ + if (mEmojiPickerShowBtn->getToggleState()) + { + mEmojiPickerShowBtn->setToggleState(false); + // Helper gets closed by focus lost event on Down before onEmojiPickerShowBtnDown + // triggers. If mEmojiHelperLastCallbackFrame is set and matches Down, means close + // was triggered by user's press. + // A bit hacky, but I can't think of a better way to handle this without rewriting helper. + mEmojiHelperLastCallbackFrame = LLFrameTimer::getFrameCount(); + } } void LLFloaterIMSessionTab::initEmojiRecentPanel() diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index bee5c8c2c4..890c920bbe 100644 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -227,6 +227,8 @@ private: void onEmojiRecentPanelToggleBtnClicked(); void onEmojiPickerShowBtnClicked(); + void onEmojiPickerShowBtnDown(); + void onEmojiPickerClosed(); void initEmojiRecentPanel(); void onEmojiRecentPanelFocusReceived(); void onEmojiRecentPanelFocusLost(); @@ -241,6 +243,9 @@ private: S32 mInputEditorPad; S32 mChatLayoutPanelHeight; S32 mFloaterHeight; + + boost::signals2::connection mEmojiCloseConn; + U32 mEmojiHelperLastCallbackFrame = { 0 }; }; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 896c7d8d70..2deb7caad5 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4189,6 +4189,32 @@ void LLFolderBridge::pasteLinkFromClipboard() std::vector<LLUUID> objects; LLClipboard::instance().pasteFromClipboard(objects); + if (objects.size() == 0) + { + LLClipboard::instance().setCutMode(false); + return; + } + + LLUUID& first_id = objects[0]; + LLInventoryItem* item = model->getItem(first_id); + if (item && item->getAssetUUID().isNull()) + { + if (item->getActualType() == LLAssetType::AT_NOTECARD) + { + // otehrwise AIS will return 'Cannot link to items with a NULL asset_id.' + LLNotificationsUtil::add("CantLinkNotecard"); + LLClipboard::instance().setCutMode(false); + return; + } + else if (item->getActualType() == LLAssetType::AT_MATERIAL) + { + LLNotificationsUtil::add("CantLinkMaterial"); + LLClipboard::instance().setCutMode(false); + return; + } + } + + LLPointer<LLInventoryCallback> cb = NULL; LLInventoryPanel* panel = mInventoryPanel.get(); if (panel->getRootFolder()->isSingleFolderMode()) diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index c4f93cee98..03bafa48bd 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -2105,6 +2105,30 @@ void LLInventoryGallery::pasteAsLink() std::vector<LLUUID> objects; LLClipboard::instance().pasteFromClipboard(objects); + if (objects.size() == 0) + { + LLClipboard::instance().setCutMode(false); + return; + } + + LLUUID& first_id = objects[0]; + LLInventoryItem* item = gInventory.getItem(first_id); + if (item && item->getAssetUUID().isNull()) + { + if (item->getActualType() == LLAssetType::AT_NOTECARD) + { + LLNotificationsUtil::add("CantLinkNotecard"); + LLClipboard::instance().setCutMode(false); + return; + } + else if (item->getActualType() == LLAssetType::AT_MATERIAL) + { + LLNotificationsUtil::add("CantLinkMaterial"); + LLClipboard::instance().setCutMode(false); + return; + } + } + bool paste_into_root = mSelectedItemIDs.empty(); for (LLUUID& dest : mSelectedItemIDs) { diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index c84b9abba0..340ecfcbbc 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -598,20 +598,23 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men bool has_children = false; bool is_full_perm_item = false; bool is_copyable = false; - LLViewerInventoryItem* selected_item = gInventory.getItem(selected_id); + + LLViewerInventoryCategory* selected_category = nullptr; + LLViewerInventoryItem* selected_item = nullptr; if(is_folder) { - LLInventoryCategory* category = gInventory.getCategory(selected_id); - if (category) + selected_category = dynamic_cast<LLViewerInventoryCategory*>(obj); + if (selected_category) { - folder_type = category->getPreferredType(); + folder_type = selected_category->getPreferredType(); is_system_folder = LLFolderType::lookupIsProtectedType(folder_type); has_children = (gInventory.categoryHasChildren(selected_id) != LLInventoryModel::CHILDREN_NO); } } else { + selected_item = dynamic_cast<LLViewerInventoryItem*>(obj); if (selected_item) { is_full_perm_item = selected_item->getIsFullPerm(); @@ -730,8 +733,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { if (is_agent_inventory && !is_inbox && !is_cof && !is_in_favorites && !is_outfits) { - LLViewerInventoryCategory* category = gInventory.getCategory(selected_id); - if (!category || !LLFriendCardsManager::instance().isCategoryInFriendFolder(category)) + if (!selected_category || !LLFriendCardsManager::instance().isCategoryInFriendFolder(selected_category)) { items.push_back(std::string("New Folder")); } @@ -759,6 +761,22 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men if (inventory_linking) { items.push_back(std::string("Paste As Link")); + + if (selected_item) + { + if (!LLAssetType::lookupCanLink(selected_item->getActualType())) + { + disabled_items.push_back(std::string("Paste As Link")); + } + else if (gInventory.isObjectDescendentOf(selected_item->getUUID(), gInventory.getLibraryRootFolderID())) + { + disabled_items.push_back(std::string("Paste As Link")); + } + } + else if (selected_category && LLFolderType::lookupIsProtectedType(selected_category->getPreferredType())) + { + disabled_items.push_back(std::string("Paste As Link")); + } } } if (is_folder && is_agent_inventory) @@ -979,9 +997,8 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { if (is_folder) { - LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id); - if (cat - && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) + if (selected_category + && !LLFolderType::lookupIsProtectedType(selected_category->getPreferredType()) && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID())) { can_list = true; diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 071f6458c5..bc817765cf 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6532,6 +6532,22 @@ Do you want to replace it with the selected object? </notification> <notification + icon="alertmodal.tga" + name="CantLinkNotecard" + type="alertmodal"> + You must save the notecard before creating a link to it. + <tag>fail</tag> + </notification> + + <notification + icon="alertmodal.tga" + name="CantLinkMaterial" + type="alertmodal"> + You must save the material before creating a link to it. + <tag>fail</tag> + </notification> + + <notification icon="alert.tga" label="Do Not Disturb Mode Warning" name="DoNotDisturbModePay" diff --git a/indra/test/lltut.h b/indra/test/lltut.h index fbf60444be..986bdd0619 100644 --- a/indra/test/lltut.h +++ b/indra/test/lltut.h @@ -30,8 +30,10 @@ #define LL_LLTUT_H #include "is_approx_equal_fraction.h" // instead of llmath.h +#include "stringize.h" #include <cstring> #include <string> +#include <string_view> #include <vector> class LLDate; @@ -89,49 +91,38 @@ namespace tut // The functions BELOW this point actually consume tut.hpp functionality. namespace tut { - inline void ensure_approximately_equals(const char* msg, F64 actual, F64 expected, U32 frac_bits) + template <typename F> // replace with C++20 floating-point concept + inline void ensure_approximately_equals(std::string_view msg, F actual, F expected, U32 frac_bits) { if(!is_approx_equal_fraction(actual, expected, frac_bits)) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal actual: ", + actual, " expected: ", expected)); } } - inline void ensure_approximately_equals(const char* msg, F32 actual, F32 expected, U32 frac_bits) + template <typename F> // replace with C++20 floating-point concept + inline void ensure_approximately_equals(F actual, F expected, U32 frac_bits) { - if(!is_approx_equal_fraction(actual, expected, frac_bits)) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); - } - } - - inline void ensure_approximately_equals(F32 actual, F32 expected, U32 frac_bits) - { - ensure_approximately_equals(NULL, actual, expected, frac_bits); + ensure_approximately_equals("", actual, expected, frac_bits); } - inline void ensure_approximately_equals_range(const char *msg, F32 actual, F32 expected, F32 delta) + template <typename F> // replace with C++20 floating-point concept + inline void ensure_approximately_equals_range(std::string_view msg, F actual, F expected, F delta) { if (fabs(actual-expected)>delta) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected << " tolerance: " << delta; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal actual: ", + actual, " expected: ", expected, " tolerance: ", delta)); } } - inline void ensure_memory_matches(const char* msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) + inline void ensure_memory_matches(std::string_view msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) { if((expected_len != actual_len) || (std::memcmp(actual, expected, actual_len) != 0)) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal"; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal")); } } @@ -141,20 +132,18 @@ namespace tut } template <class T,class Q> - void ensure_not_equals(const char* msg,const Q& actual,const T& expected) + void ensure_not_equals(std::string_view msg,const Q& actual,const T& expected) { if( expected == actual ) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "both equal " << expected; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "both equal ", expected)); } } template <class T,class Q> void ensure_not_equals(const Q& actual,const T& expected) { - ensure_not_equals(NULL, actual, expected); + ensure_not_equals("", actual, expected); } } |