/** * @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_complete"; constexpr S32 HELPER_FLOATER_OFFSET_X = 20; 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) { S32 shortCodePos = cursorPos; 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 = (L':' == wtext[shortCodePos - 1]) && (cursorPos - shortCodePos >= 2); 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 cb) { // Commit immediately if the user already typed a full shortcode if (const auto* emojiDescrp = LLEmojiDictionary::instance().getDescriptorFromShortCode(short_code)) { cb(LLWString(1, 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())); }, 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 rct = pHelperFloater->getRect(); rct.setLeftTopAndSize(floater_x - HELPER_FLOATER_OFFSET_X, floater_y - HELPER_FLOATER_OFFSET_Y + rct.getHeight(), rct.getWidth(), rct.getHeight()); pHelperFloater->setRect(rct); pHelperFloater->openFloater(LLSD().with("hint", short_code)); } void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p) { if (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(const LLWString& wstr) { if (!mHostHandle.isDead() && mEmojiCommitCb) { mEmojiCommitCb(wstr); } } 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()); })); } } }