From d95571cf7cd319e17338bc509d46ab5eab4eb968 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Sun, 23 Oct 2022 16:10:06 +0200 Subject: Add mini emoji (auto-)complete helper --- indra/newview/llpanelemojicomplete.cpp | 210 +++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 indra/newview/llpanelemojicomplete.cpp (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp new file mode 100644 index 0000000000..e1d80b62e2 --- /dev/null +++ b/indra/newview/llpanelemojicomplete.cpp @@ -0,0 +1,210 @@ +/** +* @file llpanelemojicomplete.h +* @brief Header file for LLPanelEmojiComplete +* +* $LicenseInfo:firstyear=2012&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, 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 "llemojidictionary.h" +#include "llpanelemojicomplete.h" +#include "lluictrlfactory.h" + +constexpr U32 MIN_MOUSE_MOVE_DELTA = 4; + +// ============================================================================ +// LLPanelEmojiComplete +// + +static LLDefaultChildRegistry::Register r("emoji_complete"); + +LLPanelEmojiComplete::Params::Params() + : selected_image("selected_image") +{ +} + +LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p) + : LLUICtrl(p) + , mSelectedImage(p.selected_image) +{ + setFont(p.font); +} + +LLPanelEmojiComplete::~LLPanelEmojiComplete() +{ +} + +void LLPanelEmojiComplete::draw() +{ + if (!mEmojis.empty()) + { + const S32 centerY = mRenderRect.getCenterY(); + const size_t firstVisibleIdx = mScrollPos, lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mEmojis.size()) - 1; + + if (mCurSelected >= firstVisibleIdx && mCurSelected <= lastVisibleIdx) + { + const S32 emoji_left = mRenderRect.mLeft + (mCurSelected - firstVisibleIdx) * mEmojiWidth; + const S32 emoji_height = mFont->getLineHeight() + mPadding; + mSelectedImage->draw(emoji_left, centerY - emoji_height / 2, mEmojiWidth, emoji_height); + } + + U32 left = mRenderRect.mLeft + mPadding; + for (U32 curIdx = firstVisibleIdx; curIdx <= lastVisibleIdx; curIdx++) + { + mFont->render( + mEmojis, curIdx, + left, centerY, + LLColor4::white, LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW_SOFT, + 1, S32_MAX, nullptr, false, true); + left += mEmojiWidth; + } + } +} + +BOOL LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask) +{ + LLVector2 curHover(x, y); + if ((mLastHover - curHover).lengthSquared() > MIN_MOUSE_MOVE_DELTA) + { + mCurSelected = posToIndex(x, y); + mLastHover = curHover; + } + + return TRUE; +} + +BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ + if (MASK_NONE == mask) + { + bool handled = false; + switch (key) + { + case KEY_LEFT: + case KEY_UP: + selectPrevious(); + handled = true; + break; + case KEY_RIGHT: + case KEY_DOWN: + selectNext(); + handled = true; + break; + } + return handled; + } + + return false; +} + +void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLUICtrl::reshape(width, height, called_from_parent); + updateConstraints(); +} + +void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) +{ + mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); + mScrollPos = llmin(mScrollPos, mEmojis.size()); + updateConstraints(); +} + +size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const +{ + if (mRenderRect.pointInRect(x, y)) + { + return llmin((size_t)x / mEmojiWidth, mEmojis.size() - 1); + } + return npos; +} + +void LLPanelEmojiComplete::select(size_t emoji_idx) +{ + mCurSelected = llclamp(emoji_idx, 0, mEmojis.size()); + updateScrollPos(); +} + +void LLPanelEmojiComplete::selectNext() +{ + select(mCurSelected + 1 < mEmojis.size() ? mCurSelected + 1 : 0); +} + +void LLPanelEmojiComplete::selectPrevious() +{ + select(mCurSelected - 1 >= 0 ? mCurSelected - 1 : mEmojis.size() - 1); +} + +void LLPanelEmojiComplete::setFont(const LLFontGL* fontp) +{ + mFont = fontp; + updateConstraints(); +} + +void LLPanelEmojiComplete::updateConstraints() +{ + const S32 ctrlWidth = getLocalRect().getWidth(); + + mEmojiWidth = mFont->getWidthF32(u8"\U0001F431") + mPadding * 2; + mVisibleEmojis = ctrlWidth / mEmojiWidth; + mRenderRect = getLocalRect().stretch((ctrlWidth - mVisibleEmojis * mEmojiWidth) / -2, 0); + + updateScrollPos(); +} + +void LLPanelEmojiComplete::updateScrollPos() +{ + const size_t cntEmoji = mEmojis.size(); + if (0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected) + { + mScrollPos = 0; + } + else if (cntEmoji - 1 == mCurSelected) + { + mScrollPos = mCurSelected - mVisibleEmojis + 1; + } + else + { + mScrollPos = mCurSelected - ((float)mCurSelected / (cntEmoji - 2) * (mVisibleEmojis - 2)); + } +} + +// ============================================================================ +// LLFloaterEmojiComplete +// + +LLFloaterEmojiComplete::LLFloaterEmojiComplete(const LLSD& sdKey) + : LLFloater(sdKey) +{ + // This floater should hover on top of our dependent (with the dependent having the focus) + setFocusStealsFrontmost(false); + setAutoFocus(false); + setBackgroundVisible(false); +} + +void LLFloaterEmojiComplete::onOpen(const LLSD& key) +{ + findChild("emoji_complete_ctrl")->setEmojiHint(key["hint"].asString()); +} + +// ============================================================================ -- cgit v1.2.3 From 90e272993bb4c591fd2a98772d7fe1104dbff921 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Sun, 23 Oct 2022 16:24:25 +0200 Subject: Autosize the mini emoji helper to fit the number of shown emojis --- indra/newview/llpanelemojicomplete.cpp | 49 +++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index e1d80b62e2..f6823befac 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -39,12 +39,18 @@ constexpr U32 MIN_MOUSE_MOVE_DELTA = 4; static LLDefaultChildRegistry::Register r("emoji_complete"); LLPanelEmojiComplete::Params::Params() - : selected_image("selected_image") + : autosize("autosize") + , max_emoji("max_emoji") + , padding("padding") + , selected_image("selected_image") { } LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p) : LLUICtrl(p) + , mAutoSize(p.autosize) + , mMaxVisible(p.max_emoji) + , mPadding(p.padding) , mSelectedImage(p.selected_image) { setFont(p.font); @@ -125,9 +131,23 @@ void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_paren void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) { + llwchar curEmoji = (mCurSelected < mEmojis.size()) ? mEmojis.at(mCurSelected) : 0; + size_t curEmojiIdx = (curEmoji) ? mEmojis.find(curEmoji) : std::string::npos; + mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); + mCurSelected = (std::string::npos != curEmojiIdx) ? curEmojiIdx : 0; + + if (mAutoSize) + { + mVisibleEmojis = std::min(mEmojis.size(), mMaxVisible); + reshape(mVisibleEmojis * mEmojiWidth, getRect().getHeight(), false); + } + else + { + updateConstraints(); + } + mScrollPos = llmin(mScrollPos, mEmojis.size()); - updateConstraints(); } size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const @@ -200,11 +220,34 @@ LLFloaterEmojiComplete::LLFloaterEmojiComplete(const LLSD& sdKey) setFocusStealsFrontmost(false); setAutoFocus(false); setBackgroundVisible(false); + setIsChrome(true); } void LLFloaterEmojiComplete::onOpen(const LLSD& key) { - findChild("emoji_complete_ctrl")->setEmojiHint(key["hint"].asString()); + mEmojiCtrl->setEmojiHint(key["hint"].asString()); +} + +BOOL LLFloaterEmojiComplete::postBuild() +{ + mEmojiCtrl = findChild("emoji_complete_ctrl"); + mEmojiCtrlHorz = getRect().getWidth() - mEmojiCtrl->getRect().getWidth(); + + return TRUE; +} + +void LLFloaterEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + if (!called_from_parent) + { + LLRect rctFloater = getRect(), rctCtrl = mEmojiCtrl->getRect(); + rctFloater.mRight = rctFloater.mLeft + rctCtrl.getWidth() + mEmojiCtrlHorz; + setRect(rctFloater); + + return; + } + + LLFloater::reshape(width, height, called_from_parent); } // ============================================================================ -- cgit v1.2.3 From 58cdcd5dd23778c6fad9fa0da31b670ceff8eeeb Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Sun, 23 Oct 2022 17:47:51 +0200 Subject: Handle return and escape in the mini emoji helper --- indra/newview/llpanelemojicomplete.cpp | 48 +++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index f6823befac..61b08ad48d 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llemojidictionary.h" +#include "llemojihelper.h" #include "llpanelemojicomplete.h" #include "lluictrlfactory.h" @@ -101,9 +102,9 @@ BOOL LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask) BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent) { + bool handled = false; if (MASK_NONE == mask) { - bool handled = false; switch (key) { case KEY_LEFT: @@ -116,11 +117,24 @@ BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent selectNext(); handled = true; break; + case KEY_RETURN: + if (!mEmojis.empty()) + { + LLWString wstr; + wstr.push_back(mEmojis.at(mCurSelected)); + setValue(wstring_to_utf8str(wstr)); + onCommit(); + handled = true; + } + break; } - return handled; } - return false; + if (handled) + { + return TRUE; + } + return LLUICtrl::handleKey(key, mask, called_from_parent); } void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) @@ -223,6 +237,26 @@ LLFloaterEmojiComplete::LLFloaterEmojiComplete(const LLSD& sdKey) setIsChrome(true); } +BOOL LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ + bool handled = false; + if (MASK_NONE == mask) + { + switch (key) + { + case KEY_ESCAPE: + LLEmojiHelper::instance().hideHelper(); + handled = true; + break; + } + + } + + if (handled) + return TRUE; + return LLFloater::handleKey(key, mask, called_from_parent); +} + void LLFloaterEmojiComplete::onOpen(const LLSD& key) { mEmojiCtrl->setEmojiHint(key["hint"].asString()); @@ -231,9 +265,15 @@ void LLFloaterEmojiComplete::onOpen(const LLSD& key) BOOL LLFloaterEmojiComplete::postBuild() { mEmojiCtrl = findChild("emoji_complete_ctrl"); + mEmojiCtrl->setCommitCallback( + std::bind([&](const LLSD& sdValue) + { + setValue(sdValue); + onCommit(); + }, std::placeholders::_2)); mEmojiCtrlHorz = getRect().getWidth() - mEmojiCtrl->getRect().getWidth(); - return TRUE; + return LLFloater::postBuild(); } void LLFloaterEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) -- cgit v1.2.3 From cb0bb91108b628e1134f1f655eaca9d305961cda Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Sun, 23 Oct 2022 17:51:22 +0200 Subject: Mini emoji helper shows as a small slice when there are no matching emojis --- indra/newview/llpanelemojicomplete.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 61b08ad48d..a7058a6724 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -260,6 +260,10 @@ BOOL LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_pare void LLFloaterEmojiComplete::onOpen(const LLSD& key) { mEmojiCtrl->setEmojiHint(key["hint"].asString()); + if (0 == mEmojiCtrl->getEmojiCount()) + { + LLEmojiHelper::instance().hideHelper(); + } } BOOL LLFloaterEmojiComplete::postBuild() -- cgit v1.2.3 From 81dd143d0d901e8e5234cff01fbda246e4621628 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Tue, 8 Nov 2022 00:16:58 +0100 Subject: [FIXED] Various minor issues - Typing :+1: doesn't replace the short code with the thumbs-up emoji - Moving the mouse over the emoji complete panel highlights the wrong emoji when mScrollPos > 0 - Emoji complete panel is missing attributes - Crash when attempting to show the tooltip for an emoji text segment - Emoji autocomplete panel can sometimes show empty (type ':cat', select the heart eyed one, Ctrl-Z and then type 2 which should show the emoji for :cat2 but shows an empty square instead) --- indra/newview/llpanelemojicomplete.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index a7058a6724..f890a14e8e 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -146,9 +146,9 @@ void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_paren void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) { llwchar curEmoji = (mCurSelected < mEmojis.size()) ? mEmojis.at(mCurSelected) : 0; - size_t curEmojiIdx = (curEmoji) ? mEmojis.find(curEmoji) : std::string::npos; mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); + size_t curEmojiIdx = (curEmoji) ? mEmojis.find(curEmoji) : std::string::npos; mCurSelected = (std::string::npos != curEmojiIdx) ? curEmojiIdx : 0; if (mAutoSize) @@ -168,7 +168,7 @@ size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const { if (mRenderRect.pointInRect(x, y)) { - return llmin((size_t)x / mEmojiWidth, mEmojis.size() - 1); + return mScrollPos + llmin((size_t)x / mEmojiWidth, mEmojis.size() - 1); } return npos; } -- cgit v1.2.3 From 17a3924a28770e6910022ad8f627bb8e2b667730 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Tue, 8 Nov 2022 01:10:09 +0100 Subject: Add proper mouse down handler to the emoji complete panel -> the previous commit didn't properly set mFrontChild after restoring the topmost floaters -> additionally we don't want mouse clicks in "can't steal focus from frontmost" floaters to set focus to them --- indra/newview/llpanelemojicomplete.cpp | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index f890a14e8e..8b89e3aa14 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -120,9 +120,6 @@ BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent case KEY_RETURN: if (!mEmojis.empty()) { - LLWString wstr; - wstr.push_back(mEmojis.at(mCurSelected)); - setValue(wstring_to_utf8str(wstr)); onCommit(); handled = true; } @@ -137,6 +134,33 @@ BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent return LLUICtrl::handleKey(key, mask, called_from_parent); } +BOOL LLPanelEmojiComplete::handleMouseDown(S32 x, S32 y, MASK mask) +{ + mCurSelected = posToIndex(x, y); + mLastHover = LLVector2(x, y); + + return TRUE; +} + +BOOL LLPanelEmojiComplete::handleMouseUp(S32 x, S32 y, MASK mask) +{ + mCurSelected = posToIndex(x, y); + onCommit(); + + return TRUE; +} + +void LLPanelEmojiComplete::onCommit() +{ + if (npos != mCurSelected) + { + LLWString wstr; + wstr.push_back(mEmojis.at(mCurSelected)); + setValue(wstring_to_utf8str(wstr)); + LLUICtrl::onCommit(); + } +} + void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) { LLUICtrl::reshape(width, height, called_from_parent); -- cgit v1.2.3 From b9de65d2750c0f5632116864af792c40078830ab Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Tue, 29 Aug 2023 15:07:41 +0200 Subject: SL-20209 Add 'noscroll' parameter to LLPanelEmojiComplete::Params --- indra/newview/llpanelemojicomplete.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 8b89e3aa14..8fc84c0387 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -41,6 +41,7 @@ static LLDefaultChildRegistry::Register r("emoji_complete" LLPanelEmojiComplete::Params::Params() : autosize("autosize") + , noscroll("noscroll") , max_emoji("max_emoji") , padding("padding") , selected_image("selected_image") @@ -50,6 +51,7 @@ LLPanelEmojiComplete::Params::Params() LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p) : LLUICtrl(p) , mAutoSize(p.autosize) + , mNoScroll(p.noscroll) , mMaxVisible(p.max_emoji) , mPadding(p.padding) , mSelectedImage(p.selected_image) @@ -152,7 +154,7 @@ BOOL LLPanelEmojiComplete::handleMouseUp(S32 x, S32 y, MASK mask) void LLPanelEmojiComplete::onCommit() { - if (npos != mCurSelected) + if (mCurSelected < mEmojis.size()) { LLWString wstr; wstr.push_back(mEmojis.at(mCurSelected)); @@ -167,6 +169,14 @@ void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_paren updateConstraints(); } +void LLPanelEmojiComplete::setEmojis(const LLWString& emojis) +{ + mEmojis = emojis; + mCurSelected = 0; + + onEmojisChanged(); +} + void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) { llwchar curEmoji = (mCurSelected < mEmojis.size()) ? mEmojis.at(mCurSelected) : 0; @@ -175,6 +185,11 @@ void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) size_t curEmojiIdx = (curEmoji) ? mEmojis.find(curEmoji) : std::string::npos; mCurSelected = (std::string::npos != curEmojiIdx) ? curEmojiIdx : 0; + onEmojisChanged(); +} + +void LLPanelEmojiComplete::onEmojisChanged() +{ if (mAutoSize) { mVisibleEmojis = std::min(mEmojis.size(), mMaxVisible); @@ -233,9 +248,13 @@ void LLPanelEmojiComplete::updateConstraints() void LLPanelEmojiComplete::updateScrollPos() { const size_t cntEmoji = mEmojis.size(); - if (0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected) + if (mNoScroll || 0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected) { mScrollPos = 0; + if (mCurSelected >= mVisibleEmojis) + { + mCurSelected = mVisibleEmojis ? mVisibleEmojis - 1 : 0; + } } else if (cntEmoji - 1 == mCurSelected) { @@ -273,11 +292,11 @@ BOOL LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_pare handled = true; break; } - } if (handled) return TRUE; + return LLFloater::handleKey(key, mask, called_from_parent); } -- cgit v1.2.3 From be655fef7f1f5717df73dedf84e84b73d246a0ec Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Tue, 3 Oct 2023 13:54:07 +0200 Subject: :x --- indra/newview/llpanelemojicomplete.cpp | 181 +++++++++++++++++++++++++-------- 1 file changed, 136 insertions(+), 45 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 8fc84c0387..732531691b 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -32,6 +32,7 @@ #include "lluictrlfactory.h" constexpr U32 MIN_MOUSE_MOVE_DELTA = 4; +constexpr U32 MIN_SHORT_CODE_WIDTH = 100; // ============================================================================ // LLPanelEmojiComplete @@ -42,6 +43,7 @@ static LLDefaultChildRegistry::Register r("emoji_complete" LLPanelEmojiComplete::Params::Params() : autosize("autosize") , noscroll("noscroll") + , vertical("vertical") , max_emoji("max_emoji") , padding("padding") , selected_image("selected_image") @@ -52,11 +54,13 @@ LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p : LLUICtrl(p) , mAutoSize(p.autosize) , mNoScroll(p.noscroll) + , mVertical(p.vertical) , mMaxVisible(p.max_emoji) , mPadding(p.padding) , mSelectedImage(p.selected_image) + , mIconFont(LLFontGL::getFontEmojiHuge()) + , mTextFont(LLFontGL::getFontSansSerifBig()) { - setFont(p.font); } LLPanelEmojiComplete::~LLPanelEmojiComplete() @@ -65,29 +69,61 @@ LLPanelEmojiComplete::~LLPanelEmojiComplete() void LLPanelEmojiComplete::draw() { - if (!mEmojis.empty()) - { - const S32 centerY = mRenderRect.getCenterY(); - const size_t firstVisibleIdx = mScrollPos, lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mEmojis.size()) - 1; - - if (mCurSelected >= firstVisibleIdx && mCurSelected <= lastVisibleIdx) - { - const S32 emoji_left = mRenderRect.mLeft + (mCurSelected - firstVisibleIdx) * mEmojiWidth; - const S32 emoji_height = mFont->getLineHeight() + mPadding; - mSelectedImage->draw(emoji_left, centerY - emoji_height / 2, mEmojiWidth, emoji_height); - } - - U32 left = mRenderRect.mLeft + mPadding; - for (U32 curIdx = firstVisibleIdx; curIdx <= lastVisibleIdx; curIdx++) - { - mFont->render( - mEmojis, curIdx, - left, centerY, - LLColor4::white, LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW_SOFT, - 1, S32_MAX, nullptr, false, true); - left += mEmojiWidth; - } - } + if (mEmojis.empty()) + return; + + const size_t firstVisibleIdx = mScrollPos; + const size_t lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mEmojis.size()) - 1; + + if (mCurSelected >= firstVisibleIdx && mCurSelected <= lastVisibleIdx) + { + S32 x, y, width, height; + if (mVertical) + { + x = mRenderRect.mLeft; + y = mRenderRect.mTop - (mCurSelected - firstVisibleIdx + 1) * mEmojiHeight; + width = mRenderRect.getWidth(); + height = mEmojiHeight; + } + else + { + x = mRenderRect.mLeft + (mCurSelected - firstVisibleIdx) * mEmojiWidth; + y = mRenderRect.mBottom; + width = mEmojiWidth; + height = mRenderRect.getHeight(); + } + mSelectedImage->draw(x, y, width, height); + } + + S32 iconCenterX = mRenderRect.mLeft + mEmojiWidth / 2; + S32 iconCenterY = mRenderRect.mTop - mEmojiHeight / 2; + S32 textLeft = mVertical ? mRenderRect.mLeft + mEmojiWidth + mPadding : 0; + S32 textWidth = mVertical ? getRect().getWidth() - textLeft - mPadding : 0; + + for (U32 curIdx = firstVisibleIdx; curIdx <= lastVisibleIdx; curIdx++) + { + mIconFont->render(mEmojis, curIdx, iconCenterX, iconCenterY, + LLColor4::white, LLFontGL::HCENTER, LLFontGL::VCENTER, LLFontGL::NORMAL, + LLFontGL::DROP_SHADOW_SOFT, 1, S32_MAX, nullptr, false, true); + if (mVertical) + { + llwchar emoji = mEmojis[curIdx]; + auto& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); + auto it = emoji2descr.find(emoji); + if (it != emoji2descr.end()) + { + const std::string& shortCode = it->second->ShortCodes.front(); + mTextFont->renderUTF8(shortCode, 0, textLeft, iconCenterY, LLColor4::white, + LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + shortCode.size(), textWidth, NULL, FALSE, FALSE); + } + iconCenterY -= mEmojiHeight; + } + else + { + iconCenterX += mEmojiWidth; + } + } } BOOL LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask) @@ -182,18 +218,49 @@ void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) llwchar curEmoji = (mCurSelected < mEmojis.size()) ? mEmojis.at(mCurSelected) : 0; mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); - size_t curEmojiIdx = (curEmoji) ? mEmojis.find(curEmoji) : std::string::npos; + size_t curEmojiIdx = curEmoji ? mEmojis.find(curEmoji) : std::string::npos; mCurSelected = (std::string::npos != curEmojiIdx) ? curEmojiIdx : 0; onEmojisChanged(); } +U32 LLPanelEmojiComplete::getMaxShortCodeWidth() const +{ + U32 max_width = 0; + auto& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); + for (llwchar emoji : mEmojis) + { + auto it = emoji2descr.find(emoji); + if (it != emoji2descr.end()) + { + const std::string& shortCode = it->second->ShortCodes.front(); + S32 width = mTextFont->getWidth(shortCode); + if (width > max_width) + { + max_width = width; + } + } + } + return max_width; +} + void LLPanelEmojiComplete::onEmojisChanged() { if (mAutoSize) { mVisibleEmojis = std::min(mEmojis.size(), mMaxVisible); - reshape(mVisibleEmojis * mEmojiWidth, getRect().getHeight(), false); + if (mVertical) + { + U32 maxShortCodeWidth = getMaxShortCodeWidth(); + U32 shortCodeWidth = std::max(maxShortCodeWidth, MIN_SHORT_CODE_WIDTH); + S32 width = mEmojiWidth + shortCodeWidth + mPadding * 2; + reshape(width, mVisibleEmojis * mEmojiHeight, false); + } + else + { + S32 height = getRect().getHeight(); + reshape(mVisibleEmojis * mEmojiWidth, height, false); + } } else { @@ -207,7 +274,8 @@ size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const { if (mRenderRect.pointInRect(x, y)) { - return mScrollPos + llmin((size_t)x / mEmojiWidth, mEmojis.size() - 1); + U32 pos = mVertical ? (U32)(mRenderRect.mTop - y) / mEmojiHeight : x / mEmojiWidth; + return mScrollPos + llmin((size_t)pos, mEmojis.size() - 1); } return npos; } @@ -228,21 +296,32 @@ void LLPanelEmojiComplete::selectPrevious() select(mCurSelected - 1 >= 0 ? mCurSelected - 1 : mEmojis.size() - 1); } -void LLPanelEmojiComplete::setFont(const LLFontGL* fontp) -{ - mFont = fontp; - updateConstraints(); -} - void LLPanelEmojiComplete::updateConstraints() { - const S32 ctrlWidth = getLocalRect().getWidth(); - - mEmojiWidth = mFont->getWidthF32(u8"\U0001F431") + mPadding * 2; - mVisibleEmojis = ctrlWidth / mEmojiWidth; - mRenderRect = getLocalRect().stretch((ctrlWidth - mVisibleEmojis * mEmojiWidth) / -2, 0); - - updateScrollPos(); + mRenderRect = getLocalRect(); + S32 ctrlWidth = mRenderRect.getWidth(); + S32 ctrlHeight = mRenderRect.getHeight(); + + mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; + mEmojiWidth = mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2; + if (mVertical) + { + mVisibleEmojis = ctrlHeight / mEmojiHeight; + mRenderRect.mBottom = mRenderRect.mTop - mVisibleEmojis * mEmojiHeight; + } + else + { + mVisibleEmojis = ctrlWidth / mEmojiWidth; + S32 padding = (ctrlWidth - mVisibleEmojis * mEmojiWidth) / 2; + mRenderRect.mLeft += padding; + mRenderRect.mRight -= padding; + if (mEmojiHeight > ctrlHeight) + { + mEmojiHeight = ctrlHeight; + } + } + + updateScrollPos(); } void LLPanelEmojiComplete::updateScrollPos() @@ -302,11 +381,23 @@ BOOL LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_pare void LLFloaterEmojiComplete::onOpen(const LLSD& key) { - mEmojiCtrl->setEmojiHint(key["hint"].asString()); - if (0 == mEmojiCtrl->getEmojiCount()) - { - LLEmojiHelper::instance().hideHelper(); - } + mEmojiCtrl->setEmojiHint(key["hint"].asString()); + if (0 == mEmojiCtrl->getEmojiCount()) + { + LLEmojiHelper::instance().hideHelper(); + return; + } + + if (mEmojiCtrl->isAutoSize()) + { + LLRect outer_rect = getRect(); + const LLRect& inner_rect = mEmojiCtrl->getRect(); + outer_rect.mTop = outer_rect.mBottom + inner_rect.mBottom * 2 + inner_rect.getHeight(); + outer_rect.mRight = outer_rect.mLeft + inner_rect.mLeft * 2 + inner_rect.getWidth(); + setRect(outer_rect); + } + + gFloaterView->adjustToFitScreen(this, FALSE); } BOOL LLFloaterEmojiComplete::postBuild() -- cgit v1.2.3 From 9f8763cae1ccb3577a2cd148ffc5cee564a2df65 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Wed, 4 Oct 2023 22:30:48 +0200 Subject: SL-20402 Emoji Completion floater - use vertical scrollbar when needed --- indra/newview/llpanelemojicomplete.cpp | 449 ++++++++++++++++++++------------- 1 file changed, 274 insertions(+), 175 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 732531691b..b03f16899e 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -29,10 +29,12 @@ #include "llemojidictionary.h" #include "llemojihelper.h" #include "llpanelemojicomplete.h" +#include "llscrollbar.h" #include "lluictrlfactory.h" constexpr U32 MIN_MOUSE_MOVE_DELTA = 4; constexpr U32 MIN_SHORT_CODE_WIDTH = 100; +constexpr U32 DEF_PADDING = 8; // ============================================================================ // LLPanelEmojiComplete @@ -41,26 +43,35 @@ constexpr U32 MIN_SHORT_CODE_WIDTH = 100; static LLDefaultChildRegistry::Register r("emoji_complete"); LLPanelEmojiComplete::Params::Params() - : autosize("autosize") - , noscroll("noscroll") - , vertical("vertical") - , max_emoji("max_emoji") - , padding("padding") - , selected_image("selected_image") + : autosize("autosize") + , noscroll("noscroll") + , vertical("vertical") + , max_visible("max_visible") + , padding("padding", DEF_PADDING) + , selected_image("selected_image") { } LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p) - : LLUICtrl(p) - , mAutoSize(p.autosize) - , mNoScroll(p.noscroll) - , mVertical(p.vertical) - , mMaxVisible(p.max_emoji) - , mPadding(p.padding) - , mSelectedImage(p.selected_image) - , mIconFont(LLFontGL::getFontEmojiHuge()) - , mTextFont(LLFontGL::getFontSansSerifBig()) + : LLUICtrl(p) + , mAutoSize(p.autosize) + , mNoScroll(p.noscroll) + , mVertical(p.vertical) + , mMaxVisible(p.max_visible) + , mPadding(p.padding) + , mSelectedImage(p.selected_image) + , mIconFont(LLFontGL::getFontEmojiHuge()) + , mTextFont(LLFontGL::getFontSansSerifBig()) + , mScrollbar(nullptr) { + if (mVertical) + { + LLScrollbar::Params sbparams; + sbparams.orientation(LLScrollbar::VERTICAL); + sbparams.change_callback([this](S32 index, LLScrollbar*) { onScrollbarChange(index); }); + mScrollbar = LLUICtrlFactory::create(sbparams); + addChild(mScrollbar); + } } LLPanelEmojiComplete::~LLPanelEmojiComplete() @@ -69,13 +80,15 @@ LLPanelEmojiComplete::~LLPanelEmojiComplete() void LLPanelEmojiComplete::draw() { + LLUICtrl::draw(); + if (mEmojis.empty()) return; const size_t firstVisibleIdx = mScrollPos; - const size_t lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mEmojis.size()) - 1; + const size_t lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mTotalEmojis); - if (mCurSelected >= firstVisibleIdx && mCurSelected <= lastVisibleIdx) + if (mCurSelected >= firstVisibleIdx && mCurSelected < lastVisibleIdx) { S32 x, y, width, height; if (mVertical) @@ -100,7 +113,7 @@ void LLPanelEmojiComplete::draw() S32 textLeft = mVertical ? mRenderRect.mLeft + mEmojiWidth + mPadding : 0; S32 textWidth = mVertical ? getRect().getWidth() - textLeft - mPadding : 0; - for (U32 curIdx = firstVisibleIdx; curIdx <= lastVisibleIdx; curIdx++) + for (U32 curIdx = firstVisibleIdx; curIdx < lastVisibleIdx; curIdx++) { mIconFont->render(mEmojis, curIdx, iconCenterX, iconCenterY, LLColor4::white, LLFontGL::HCENTER, LLFontGL::VCENTER, LLFontGL::NORMAL, @@ -128,100 +141,154 @@ void LLPanelEmojiComplete::draw() BOOL LLPanelEmojiComplete::handleHover(S32 x, S32 y, MASK mask) { - LLVector2 curHover(x, y); - if ((mLastHover - curHover).lengthSquared() > MIN_MOUSE_MOVE_DELTA) - { - mCurSelected = posToIndex(x, y); - mLastHover = curHover; - } - - return TRUE; + if (mScrollbar && mScrollbar->getVisible() && childrenHandleHover(x, y, mask)) + return TRUE; + + LLVector2 curHover(x, y); + if ((mLastHover - curHover).lengthSquared() > MIN_MOUSE_MOVE_DELTA) + { + size_t index = posToIndex(x, y); + if (index < mTotalEmojis) + mCurSelected = index; + mLastHover = curHover; + } + + return TRUE; } BOOL LLPanelEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - bool handled = false; - if (MASK_NONE == mask) - { - switch (key) - { - case KEY_LEFT: - case KEY_UP: - selectPrevious(); - handled = true; - break; - case KEY_RIGHT: - case KEY_DOWN: - selectNext(); - handled = true; - break; - case KEY_RETURN: - if (!mEmojis.empty()) - { - onCommit(); - handled = true; - } - break; - } - } - - if (handled) - { - return TRUE; - } - return LLUICtrl::handleKey(key, mask, called_from_parent); + bool handled = false; + if (mTotalEmojis && MASK_NONE == mask) + { + switch (key) + { + case KEY_HOME: + select(0); + handled = true; + break; + + case KEY_END: + select(mTotalEmojis - 1); + handled = true; + break; + + case KEY_PAGE_DOWN: + select(mCurSelected + mVisibleEmojis - 1); + handled = true; + break; + + case KEY_PAGE_UP: + select(mCurSelected - llmin(mCurSelected, mVisibleEmojis + 1)); + handled = true; + break; + + case KEY_LEFT: + case KEY_UP: + selectPrevious(); + handled = true; + break; + + case KEY_RIGHT: + case KEY_DOWN: + selectNext(); + handled = true; + break; + + case KEY_RETURN: + onCommit(); + handled = true; + break; + } + } + + if (handled) + { + return TRUE; + } + + return LLUICtrl::handleKey(key, mask, called_from_parent); } BOOL LLPanelEmojiComplete::handleMouseDown(S32 x, S32 y, MASK mask) { - mCurSelected = posToIndex(x, y); - mLastHover = LLVector2(x, y); + if (mScrollbar && mScrollbar->getVisible() && childrenHandleMouseDown(x, y, mask)) + return TRUE; + + mCurSelected = posToIndex(x, y); + mLastHover = LLVector2(x, y); - return TRUE; + return TRUE; } BOOL LLPanelEmojiComplete::handleMouseUp(S32 x, S32 y, MASK mask) { - mCurSelected = posToIndex(x, y); - onCommit(); + if (mScrollbar && mScrollbar->getVisible() && childrenHandleMouseUp(x, y, mask)) + return TRUE; + + mCurSelected = posToIndex(x, y); + onCommit(); + + return TRUE; +} + +BOOL LLPanelEmojiComplete::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if (mNoScroll) + return FALSE; - return TRUE; + if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(x, y, clicks)) + { + mCurSelected = posToIndex(x, y); + return TRUE; + } + + if (mTotalEmojis > mVisibleEmojis) + { + mScrollPos = llclamp(mScrollPos + clicks, 0, mTotalEmojis - mVisibleEmojis); + mCurSelected = posToIndex(x, y); + return TRUE; + } + + return FALSE; } void LLPanelEmojiComplete::onCommit() { - if (mCurSelected < mEmojis.size()) - { - LLWString wstr; - wstr.push_back(mEmojis.at(mCurSelected)); - setValue(wstring_to_utf8str(wstr)); - LLUICtrl::onCommit(); - } + if (mCurSelected < mTotalEmojis) + { + LLWString wstr; + wstr.push_back(mEmojis.at(mCurSelected)); + setValue(wstring_to_utf8str(wstr)); + LLUICtrl::onCommit(); + } } void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) { - LLUICtrl::reshape(width, height, called_from_parent); - updateConstraints(); + LLUICtrl::reshape(width, height, called_from_parent); + updateConstraints(); } void LLPanelEmojiComplete::setEmojis(const LLWString& emojis) { - mEmojis = emojis; - mCurSelected = 0; + mEmojis = emojis; + mTotalEmojis = mEmojis.size(); + mCurSelected = 0; - onEmojisChanged(); + onEmojisChanged(); } void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) { - llwchar curEmoji = (mCurSelected < mEmojis.size()) ? mEmojis.at(mCurSelected) : 0; + llwchar curEmoji = mCurSelected < mTotalEmojis ? mEmojis.at(mCurSelected) : 0; - mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); - size_t curEmojiIdx = curEmoji ? mEmojis.find(curEmoji) : std::string::npos; - mCurSelected = (std::string::npos != curEmojiIdx) ? curEmojiIdx : 0; + mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); + mTotalEmojis = mEmojis.size(); + size_t curEmojiIdx = curEmoji ? mEmojis.find(curEmoji) : std::string::npos; + mCurSelected = std::string::npos != curEmojiIdx ? curEmojiIdx : 0; - onEmojisChanged(); + onEmojisChanged(); } U32 LLPanelEmojiComplete::getMaxShortCodeWidth() const @@ -246,79 +313,104 @@ U32 LLPanelEmojiComplete::getMaxShortCodeWidth() const void LLPanelEmojiComplete::onEmojisChanged() { - if (mAutoSize) - { - mVisibleEmojis = std::min(mEmojis.size(), mMaxVisible); + if (mAutoSize) + { + S32 width, height; + mVisibleEmojis = llmin(mTotalEmojis, mMaxVisible); if (mVertical) { U32 maxShortCodeWidth = getMaxShortCodeWidth(); - U32 shortCodeWidth = std::max(maxShortCodeWidth, MIN_SHORT_CODE_WIDTH); - S32 width = mEmojiWidth + shortCodeWidth + mPadding * 2; - reshape(width, mVisibleEmojis * mEmojiHeight, false); + U32 shortCodeWidth = llmax(maxShortCodeWidth, MIN_SHORT_CODE_WIDTH); + width = mEmojiWidth + shortCodeWidth + mPadding * 2; + if (!mNoScroll && mVisibleEmojis < mTotalEmojis) + { + width += mScrollbar->getThickness(); + } + height = mVisibleEmojis * mEmojiHeight; } else { - S32 height = getRect().getHeight(); - reshape(mVisibleEmojis * mEmojiWidth, height, false); + width = mVisibleEmojis * mEmojiWidth; + height = getRect().getHeight(); } - } - else - { - updateConstraints(); - } + LLUICtrl::reshape(width, height, false); + } + else + { + mVisibleEmojis = mVertical ? getRect().getHeight() / mEmojiHeight : getRect().getWidth() / mEmojiWidth; + } - mScrollPos = llmin(mScrollPos, mEmojis.size()); + updateConstraints(); +} + +void LLPanelEmojiComplete::onScrollbarChange(S32 index) +{ + mScrollPos = llclamp(index, 0, mTotalEmojis - mVisibleEmojis); } size_t LLPanelEmojiComplete::posToIndex(S32 x, S32 y) const { - if (mRenderRect.pointInRect(x, y)) - { - U32 pos = mVertical ? (U32)(mRenderRect.mTop - y) / mEmojiHeight : x / mEmojiWidth; - return mScrollPos + llmin((size_t)pos, mEmojis.size() - 1); - } - return npos; + if (mRenderRect.pointInRect(x, y)) + { + U32 pos = mVertical ? (U32)(mRenderRect.mTop - y) / mEmojiHeight : x / mEmojiWidth; + return llmin(mScrollPos + pos, mTotalEmojis - 1); + } + return std::string::npos; } void LLPanelEmojiComplete::select(size_t emoji_idx) { - mCurSelected = llclamp(emoji_idx, 0, mEmojis.size()); - updateScrollPos(); + mCurSelected = llclamp(emoji_idx, 0, mTotalEmojis - 1); + + updateScrollPos(); } void LLPanelEmojiComplete::selectNext() { - select(mCurSelected + 1 < mEmojis.size() ? mCurSelected + 1 : 0); + if (!mTotalEmojis) + return; + + mCurSelected = (mCurSelected < mTotalEmojis - 1) ? mCurSelected + 1 : 0; + + updateScrollPos(); } void LLPanelEmojiComplete::selectPrevious() { - select(mCurSelected - 1 >= 0 ? mCurSelected - 1 : mEmojis.size() - 1); + if (!mTotalEmojis) + return; + + mCurSelected = (mCurSelected && mCurSelected < mTotalEmojis) ? mCurSelected - 1 : mTotalEmojis - 1; + + updateScrollPos(); } void LLPanelEmojiComplete::updateConstraints() { mRenderRect = getLocalRect(); - S32 ctrlWidth = mRenderRect.getWidth(); - S32 ctrlHeight = mRenderRect.getHeight(); - mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; mEmojiWidth = mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2; if (mVertical) { - mVisibleEmojis = ctrlHeight / mEmojiHeight; - mRenderRect.mBottom = mRenderRect.mTop - mVisibleEmojis * mEmojiHeight; + mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; + if (!mNoScroll && mVisibleEmojis < mTotalEmojis) + { + mRenderRect.mRight -= mScrollbar->getThickness(); + mScrollbar->setDocSize(mTotalEmojis); + mScrollbar->setPageSize(mVisibleEmojis); + mScrollbar->setOrigin(mRenderRect.mRight, 0); + mScrollbar->reshape(mScrollbar->getThickness(), mRenderRect.mTop, TRUE); + mScrollbar->setVisible(TRUE); + } + else + { + mScrollbar->setVisible(FALSE); + } } else { - mVisibleEmojis = ctrlWidth / mEmojiWidth; - S32 padding = (ctrlWidth - mVisibleEmojis * mEmojiWidth) / 2; - mRenderRect.mLeft += padding; - mRenderRect.mRight -= padding; - if (mEmojiHeight > ctrlHeight) - { - mEmojiHeight = ctrlHeight; - } + mEmojiHeight = mRenderRect.getHeight(); + mRenderRect.stretch((mRenderRect.getWidth() - mVisibleEmojis * mEmojiWidth) / -2, 0); } updateScrollPos(); @@ -326,23 +418,27 @@ void LLPanelEmojiComplete::updateConstraints() void LLPanelEmojiComplete::updateScrollPos() { - const size_t cntEmoji = mEmojis.size(); - if (mNoScroll || 0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected) - { - mScrollPos = 0; - if (mCurSelected >= mVisibleEmojis) - { - mCurSelected = mVisibleEmojis ? mVisibleEmojis - 1 : 0; - } - } - else if (cntEmoji - 1 == mCurSelected) - { - mScrollPos = mCurSelected - mVisibleEmojis + 1; - } - else - { - mScrollPos = mCurSelected - ((float)mCurSelected / (cntEmoji - 2) * (mVisibleEmojis - 2)); - } + if (mNoScroll || 0 == mTotalEmojis || mTotalEmojis < mVisibleEmojis || 0 == mCurSelected) + { + mScrollPos = 0; + if (mCurSelected >= mVisibleEmojis) + { + mCurSelected = mVisibleEmojis ? mVisibleEmojis - 1 : 0; + } + } + else if (mTotalEmojis - 1 == mCurSelected) + { + mScrollPos = mTotalEmojis - mVisibleEmojis; + } + else + { + mScrollPos = mCurSelected - ((float)mCurSelected / (mTotalEmojis - 2) * (mVisibleEmojis - 2)); + } + + if (mScrollbar && mScrollbar->getVisible()) + { + mScrollbar->setDocPos(mScrollPos); + } } // ============================================================================ @@ -350,33 +446,33 @@ void LLPanelEmojiComplete::updateScrollPos() // LLFloaterEmojiComplete::LLFloaterEmojiComplete(const LLSD& sdKey) - : LLFloater(sdKey) + : LLFloater(sdKey) { - // This floater should hover on top of our dependent (with the dependent having the focus) - setFocusStealsFrontmost(false); - setAutoFocus(false); - setBackgroundVisible(false); - setIsChrome(true); + // This floater should hover on top of our dependent (with the dependent having the focus) + setFocusStealsFrontmost(false); + setAutoFocus(false); + setBackgroundVisible(false); + setIsChrome(true); } BOOL LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - bool handled = false; - if (MASK_NONE == mask) - { - switch (key) - { - case KEY_ESCAPE: - LLEmojiHelper::instance().hideHelper(); - handled = true; - break; - } - } - - if (handled) - return TRUE; - - return LLFloater::handleKey(key, mask, called_from_parent); + bool handled = false; + if (MASK_NONE == mask) + { + switch (key) + { + case KEY_ESCAPE: + LLEmojiHelper::instance().hideHelper(); + handled = true; + break; + } + } + + if (handled) + return TRUE; + + return LLFloater::handleKey(key, mask, called_from_parent); } void LLFloaterEmojiComplete::onOpen(const LLSD& key) @@ -402,30 +498,33 @@ void LLFloaterEmojiComplete::onOpen(const LLSD& key) BOOL LLFloaterEmojiComplete::postBuild() { - mEmojiCtrl = findChild("emoji_complete_ctrl"); - mEmojiCtrl->setCommitCallback( - std::bind([&](const LLSD& sdValue) - { - setValue(sdValue); - onCommit(); - }, std::placeholders::_2)); - mEmojiCtrlHorz = getRect().getWidth() - mEmojiCtrl->getRect().getWidth(); - - return LLFloater::postBuild(); + mEmojiCtrl = findChild("emoji_complete_ctrl"); + mEmojiCtrl->setCommitCallback( + [this](LLUICtrl* ctrl, const LLSD& param) + { + setValue(param); + onCommit(); + }); + + mEmojiCtrlHorz = getRect().getWidth() - mEmojiCtrl->getRect().getWidth(); + mEmojiCtrlVert = getRect().getHeight() - mEmojiCtrl->getRect().getHeight(); + + return LLFloater::postBuild(); } void LLFloaterEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) { - if (!called_from_parent) - { - LLRect rctFloater = getRect(), rctCtrl = mEmojiCtrl->getRect(); - rctFloater.mRight = rctFloater.mLeft + rctCtrl.getWidth() + mEmojiCtrlHorz; - setRect(rctFloater); - - return; - } - - LLFloater::reshape(width, height, called_from_parent); + if (called_from_parent) + { + LLFloater::reshape(width, height, called_from_parent); + } + else + { + LLRect outer(getRect()), inner(mEmojiCtrl->getRect()); + outer.mRight = outer.mLeft + inner.getWidth() + mEmojiCtrlHorz; + outer.mTop = outer.mBottom + inner.getHeight() + mEmojiCtrlVert; + setRect(outer); + } } // ============================================================================ -- cgit v1.2.3 From 98214577c36d9c8dd0e13c7b678a399b35450bd3 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Thu, 5 Oct 2023 11:28:54 +0200 Subject: SL-20390 Emoji Completion floater - ignore symbols in shortcodes when searching by pattern --- indra/newview/llpanelemojicomplete.cpp | 72 ++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 30 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index b03f16899e..22cac8ad88 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -82,7 +82,7 @@ void LLPanelEmojiComplete::draw() { LLUICtrl::draw(); - if (mEmojis.empty()) + if (!mTotalEmojis) return; const size_t firstVisibleIdx = mScrollPos; @@ -115,21 +115,16 @@ void LLPanelEmojiComplete::draw() for (U32 curIdx = firstVisibleIdx; curIdx < lastVisibleIdx; curIdx++) { - mIconFont->render(mEmojis, curIdx, iconCenterX, iconCenterY, + LLWString text(1, mEmojis[curIdx].Character); + mIconFont->render(text, 0, iconCenterX, iconCenterY, LLColor4::white, LLFontGL::HCENTER, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW_SOFT, 1, S32_MAX, nullptr, false, true); if (mVertical) { - llwchar emoji = mEmojis[curIdx]; - auto& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); - auto it = emoji2descr.find(emoji); - if (it != emoji2descr.end()) - { - const std::string& shortCode = it->second->ShortCodes.front(); - mTextFont->renderUTF8(shortCode, 0, textLeft, iconCenterY, LLColor4::white, - LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - shortCode.size(), textWidth, NULL, FALSE, FALSE); - } + const std::string& shortCode = mEmojis[curIdx].String; + mTextFont->renderUTF8(shortCode, 0, textLeft, iconCenterY, LLColor4::white, + LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + shortCode.size(), textWidth, NULL, FALSE, FALSE); iconCenterY -= mEmojiHeight; } else @@ -257,9 +252,8 @@ void LLPanelEmojiComplete::onCommit() { if (mCurSelected < mTotalEmojis) { - LLWString wstr; - wstr.push_back(mEmojis.at(mCurSelected)); - setValue(wstring_to_utf8str(wstr)); + LLSD value(wstring_to_utf8str(LLWString(1, mEmojis[mCurSelected].Character))); + setValue(value); LLUICtrl::onCommit(); } } @@ -272,7 +266,23 @@ void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_paren void LLPanelEmojiComplete::setEmojis(const LLWString& emojis) { - mEmojis = emojis; + mEmojis.clear(); + + auto& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); + for (const llwchar& emoji : emojis) + { + std::string shortCode; + if (mVertical) + { + auto it = emoji2descr.find(emoji); + if (it != emoji2descr.end() && !it->second->ShortCodes.empty()) + { + shortCode = it->second->ShortCodes.front(); + } + } + mEmojis.emplace_back(emoji, shortCode, 0, 0); + } + mTotalEmojis = mEmojis.size(); mCurSelected = 0; @@ -281,12 +291,20 @@ void LLPanelEmojiComplete::setEmojis(const LLWString& emojis) void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) { - llwchar curEmoji = mCurSelected < mTotalEmojis ? mEmojis.at(mCurSelected) : 0; + llwchar curEmoji = mCurSelected < mTotalEmojis ? mEmojis[mCurSelected].Character : 0; - mEmojis = LLEmojiDictionary::instance().findMatchingEmojis(hint); + LLEmojiDictionary::instance().findByShortCode(mEmojis, hint); mTotalEmojis = mEmojis.size(); - size_t curEmojiIdx = curEmoji ? mEmojis.find(curEmoji) : std::string::npos; - mCurSelected = std::string::npos != curEmojiIdx ? curEmojiIdx : 0; + + mCurSelected = 0; + for (size_t i = 1; i < mTotalEmojis; ++i) + { + if (mEmojis[i].Character == curEmoji) + { + mCurSelected = i; + break; + } + } onEmojisChanged(); } @@ -294,18 +312,12 @@ void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) U32 LLPanelEmojiComplete::getMaxShortCodeWidth() const { U32 max_width = 0; - auto& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr(); - for (llwchar emoji : mEmojis) + for (const LLEmojiSearchResult& result : mEmojis) { - auto it = emoji2descr.find(emoji); - if (it != emoji2descr.end()) + S32 width = mTextFont->getWidth(result.String); + if (width > max_width) { - const std::string& shortCode = it->second->ShortCodes.front(); - S32 width = mTextFont->getWidth(shortCode); - if (width > max_width) - { - max_width = width; - } + max_width = width; } } return max_width; -- cgit v1.2.3 From da783d1750ec8a03d62ddf81a0b44bba1b2f565b Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Thu, 5 Oct 2023 14:17:50 +0200 Subject: SL-20389 Emoji Completion floater - highlight the entered part of the shortcode --- indra/newview/llpanelemojicomplete.cpp | 38 +++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 22cac8ad88..29e1a29ed3 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -108,10 +108,10 @@ void LLPanelEmojiComplete::draw() mSelectedImage->draw(x, y, width, height); } - S32 iconCenterX = mRenderRect.mLeft + mEmojiWidth / 2; - S32 iconCenterY = mRenderRect.mTop - mEmojiHeight / 2; - S32 textLeft = mVertical ? mRenderRect.mLeft + mEmojiWidth + mPadding : 0; - S32 textWidth = mVertical ? getRect().getWidth() - textLeft - mPadding : 0; + F32 iconCenterX = mRenderRect.mLeft + (F32)mEmojiWidth / 2; + F32 iconCenterY = mRenderRect.mTop - (F32)mEmojiHeight / 2; + F32 textLeft = mVertical ? mRenderRect.mLeft + mEmojiWidth + mPadding : 0; + F32 textWidth = mVertical ? getRect().getWidth() - textLeft - mPadding : 0; for (U32 curIdx = firstVisibleIdx; curIdx < lastVisibleIdx; curIdx++) { @@ -122,9 +122,33 @@ void LLPanelEmojiComplete::draw() if (mVertical) { const std::string& shortCode = mEmojis[curIdx].String; - mTextFont->renderUTF8(shortCode, 0, textLeft, iconCenterY, LLColor4::white, - LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - shortCode.size(), textWidth, NULL, FALSE, FALSE); + F32 x0 = textLeft; + F32 x1 = textWidth; + if (mEmojis[curIdx].Begin) + { + std::string text = shortCode.substr(0, mEmojis[curIdx].Begin); + mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::white, + LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + text.size(), x1, NULL, FALSE, FALSE); + x0 += mTextFont->getWidthF32(text); + x1 = textLeft + textWidth - x0; + } + if (x1 > 0 && mEmojis[curIdx].End > mEmojis[curIdx].Begin) + { + std::string text = shortCode.substr(mEmojis[curIdx].Begin, mEmojis[curIdx].End - mEmojis[curIdx].Begin); + mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::yellow6, + LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + text.size(), x1, NULL, FALSE, FALSE); + x0 += mTextFont->getWidthF32(text); + x1 = textLeft + textWidth - x0; + } + if (x1 > 0 && mEmojis[curIdx].End < shortCode.size()) + { + std::string text = shortCode.substr(mEmojis[curIdx].End); + mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::white, + LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + text.size(), x1, NULL, FALSE, FALSE); + } iconCenterY -= mEmojiHeight; } else -- cgit v1.2.3 From 93e4d069d1a9d003664f23f842cf100f635607ed Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Wed, 8 Nov 2023 17:53:16 +0100 Subject: SL-20518 EmojiPicker - The 'More' button is overlapped the recently used emojis row after narrowing back the 'Conversations' floater --- indra/newview/llpanelemojicomplete.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 29e1a29ed3..9bfe04fc31 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -285,7 +285,14 @@ void LLPanelEmojiComplete::onCommit() void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_parent) { LLUICtrl::reshape(width, height, called_from_parent); - updateConstraints(); + if (mAutoSize) + { + updateConstraints(); + } + else + { + onEmojisChanged(); + } } void LLPanelEmojiComplete::setEmojis(const LLWString& emojis) @@ -373,7 +380,9 @@ void LLPanelEmojiComplete::onEmojisChanged() } else { - mVisibleEmojis = mVertical ? getRect().getHeight() / mEmojiHeight : getRect().getWidth() / mEmojiWidth; + mVisibleEmojis = mVertical ? + mEmojiHeight ? getRect().getHeight() / mEmojiHeight : 0 : + mEmojiWidth ? getRect().getWidth() / mEmojiWidth : 0; } updateConstraints(); -- cgit v1.2.3 From 6173bd6236dc452b05b6e32258b59c6db05a022d Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Thu, 11 Jan 2024 19:19:35 +0100 Subject: SL-20749 Scrolling up by mouse wheel is endless in LLPanelEmojiComplete --- indra/newview/llpanelemojicomplete.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 9bfe04fc31..46f455aede 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -264,7 +264,11 @@ BOOL LLPanelEmojiComplete::handleScrollWheel(S32 x, S32 y, S32 clicks) if (mTotalEmojis > mVisibleEmojis) { - mScrollPos = llclamp(mScrollPos + clicks, 0, mTotalEmojis - mVisibleEmojis); + // In case of wheel up (clicks < 0) we shouldn't subtract more than value of mScrollPos + // Example: if mScrollPos = 0, clicks = -1 then (mScrollPos + clicks) becomes SIZE_MAX + // As a result of llclamp() mScrollPos becomes (mTotalEmojis - mVisibleEmojis) + S32 newScrollPos = llmax(0, (S32)mScrollPos + clicks); + mScrollPos = llclamp((size_t)newScrollPos, 0, mTotalEmojis - mVisibleEmojis); mCurSelected = posToIndex(x, y); return TRUE; } -- cgit v1.2.3 From ae91ae43a51c58cc496f3947921fbf886c6be86e Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Mon, 15 Jan 2024 23:20:24 +0100 Subject: SL-20795 Part of previously typed emojis disappear in the 'Save settings as a preset...' option of the 'Preferences' floater --- indra/newview/llpanelemojicomplete.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/newview/llpanelemojicomplete.cpp') diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 46f455aede..e6e3a10e13 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -118,7 +118,7 @@ void LLPanelEmojiComplete::draw() LLWString text(1, mEmojis[curIdx].Character); mIconFont->render(text, 0, iconCenterX, iconCenterY, LLColor4::white, LLFontGL::HCENTER, LLFontGL::VCENTER, LLFontGL::NORMAL, - LLFontGL::DROP_SHADOW_SOFT, 1, S32_MAX, nullptr, false, true); + LLFontGL::DROP_SHADOW_SOFT, 1); if (mVertical) { const std::string& shortCode = mEmojis[curIdx].String; @@ -129,7 +129,7 @@ void LLPanelEmojiComplete::draw() std::string text = shortCode.substr(0, mEmojis[curIdx].Begin); mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::white, LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - text.size(), x1, NULL, FALSE, FALSE); + text.size(), x1); x0 += mTextFont->getWidthF32(text); x1 = textLeft + textWidth - x0; } @@ -138,7 +138,7 @@ void LLPanelEmojiComplete::draw() std::string text = shortCode.substr(mEmojis[curIdx].Begin, mEmojis[curIdx].End - mEmojis[curIdx].Begin); mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::yellow6, LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - text.size(), x1, NULL, FALSE, FALSE); + text.size(), x1); x0 += mTextFont->getWidthF32(text); x1 = textLeft + textWidth - x0; } @@ -147,7 +147,7 @@ void LLPanelEmojiComplete::draw() std::string text = shortCode.substr(mEmojis[curIdx].End); mTextFont->renderUTF8(text, 0, x0, iconCenterY, LLColor4::white, LLFontGL::LEFT, LLFontGL::VCENTER, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - text.size(), x1, NULL, FALSE, FALSE); + text.size(), x1); } iconCenterY -= mEmojiHeight; } -- cgit v1.2.3