178 lines
5.3 KiB
C++
178 lines
5.3 KiB
C++
/**
|
|
* @file llemojihelper.h
|
|
* @brief Header file for LLEmojiHelper
|
|
*
|
|
* $LicenseInfo:firstyear=2014&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2014, 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 "linden_common.h"
|
|
|
|
#include "llemojidictionary.h"
|
|
#include "llemojihelper.h"
|
|
#include "llfloater.h"
|
|
#include "llfloaterreg.h"
|
|
#include "lluictrl.h"
|
|
|
|
// ============================================================================
|
|
// Constants
|
|
//
|
|
|
|
constexpr char DEFAULT_EMOJI_HELPER_FLOATER[] = "emoji_picker";
|
|
constexpr S32 HELPER_FLOATER_OFFSET_X = 0;
|
|
constexpr S32 HELPER_FLOATER_OFFSET_Y = 0;
|
|
|
|
// ============================================================================
|
|
// LLEmojiHelper
|
|
//
|
|
|
|
std::string LLEmojiHelper::getToolTip(llwchar ch) const
|
|
{
|
|
return LLEmojiDictionary::instance().getNameFromEmoji(ch);
|
|
}
|
|
|
|
bool LLEmojiHelper::isActive(const LLUICtrl* ctrl_p) const
|
|
{
|
|
return mHostHandle.get() == ctrl_p;
|
|
}
|
|
|
|
// static
|
|
bool LLEmojiHelper::isCursorInEmojiCode(const LLWString& wtext, S32 cursorPos, S32* pShortCodePos)
|
|
{
|
|
// <FS:PP> FIRE-33735: Option to suppress emoji chooser window from automatically popping up while typing in chat bars
|
|
static LLUICachedControl<bool> FSEnableEmojiWindowPopupWhileTyping("FSEnableEmojiWindowPopupWhileTyping");
|
|
if (!FSEnableEmojiWindowPopupWhileTyping)
|
|
{
|
|
return false;
|
|
}
|
|
// </FS:PP>
|
|
if (cursorPos < 0 || wtext.size() < cursorPos) return false; // <FS:Beq/> FIRE-33773 - fix logic to avoid OOB access to wtext
|
|
// If the cursor is currently on a colon start the check one character further back
|
|
S32 shortCodePos = (cursorPos == 0 || L':' != wtext[cursorPos - 1]) ? cursorPos : cursorPos - 1;
|
|
|
|
auto isPartOfShortcode = [](llwchar ch) {
|
|
switch (ch)
|
|
{
|
|
case L'-':
|
|
case L'_':
|
|
case L'+':
|
|
return true;
|
|
default:
|
|
return LLStringOps::isAlnum(ch);
|
|
}
|
|
};
|
|
while (shortCodePos > 1 && isPartOfShortcode(wtext[shortCodePos - 1]))
|
|
{
|
|
shortCodePos--;
|
|
}
|
|
|
|
bool isShortCode = (cursorPos - shortCodePos >= 2) && (L':' == wtext[shortCodePos - 1]); // <FS:Beq/> FIRE-33773 - fix logic to avoid OOB access to wtext
|
|
if (pShortCodePos)
|
|
*pShortCodePos = (isShortCode) ? shortCodePos - 1 : -1;
|
|
return isShortCode;
|
|
}
|
|
|
|
void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(llwchar)> cb)
|
|
{
|
|
// Commit immediately if the user already typed a full shortcode
|
|
if (const auto* emojiDescrp = LLEmojiDictionary::instance().getDescriptorFromShortCode(short_code))
|
|
{
|
|
cb(emojiDescrp->Character);
|
|
hideHelper();
|
|
return;
|
|
}
|
|
|
|
if (mHelperHandle.isDead())
|
|
{
|
|
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));
|
|
}
|
|
setHostCtrl(hostctrl_p);
|
|
mEmojiCommitCb = cb;
|
|
|
|
S32 floater_x, floater_y;
|
|
if (!hostctrl_p->localPointToOtherView(local_x, local_y, &floater_x, &floater_y, gFloaterView))
|
|
{
|
|
LL_ERRS() << "Cannot show emoji helper for non-floater controls." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
LLFloater* pHelperFloater = mHelperHandle.get();
|
|
LLRect rect = pHelperFloater->getRect();
|
|
S32 left = floater_x - HELPER_FLOATER_OFFSET_X;
|
|
S32 top = floater_y - HELPER_FLOATER_OFFSET_Y + rect.getHeight();
|
|
rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight());
|
|
pHelperFloater->setRect(rect);
|
|
pHelperFloater->openFloater(LLSD().with("hint", short_code));
|
|
}
|
|
|
|
void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p, bool strict)
|
|
{
|
|
mIsHideDisabled &= !strict;
|
|
if (mIsHideDisabled || (ctrl_p && !isActive(ctrl_p)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
setHostCtrl(nullptr);
|
|
}
|
|
|
|
bool LLEmojiHelper::handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask)
|
|
{
|
|
if (mHelperHandle.isDead() || !isActive(ctrl_p))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return mHelperHandle.get()->handleKey(key, mask, true);
|
|
}
|
|
|
|
void LLEmojiHelper::onCommitEmoji(llwchar emoji)
|
|
{
|
|
if (!mHostHandle.isDead() && mEmojiCommitCb)
|
|
{
|
|
mEmojiCommitCb(emoji);
|
|
}
|
|
}
|
|
|
|
void LLEmojiHelper::setHostCtrl(LLUICtrl* hostctrl_p)
|
|
{
|
|
const LLUICtrl* pCurHostCtrl = mHostHandle.get();
|
|
if (pCurHostCtrl != hostctrl_p)
|
|
{
|
|
mHostCtrlFocusLostConn.disconnect();
|
|
mHostHandle.markDead();
|
|
mEmojiCommitCb = {};
|
|
|
|
if (!mHelperHandle.isDead())
|
|
{
|
|
mHelperHandle.get()->closeFloater();
|
|
}
|
|
|
|
if (hostctrl_p)
|
|
{
|
|
mHostHandle = hostctrl_p->getHandle();
|
|
mHostCtrlFocusLostConn = hostctrl_p->setFocusLostCallback(std::bind([&]() { hideHelper(getHostCtrl()); }));
|
|
}
|
|
}
|
|
}
|