master
Alexander Gavriliuk 2023-10-03 13:54:07 +02:00
parent c3adae2a5f
commit be655fef7f
7 changed files with 161 additions and 63 deletions

View File

@ -423,7 +423,7 @@ S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, cons
return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
}
S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses, BOOL use_color) const
S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses, BOOL use_color) const
{
return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses, use_color);
}
@ -488,7 +488,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars) const
return getWidthF32(wchars, 0, S32_MAX);
}
F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars ) const
F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max_chars) const
{
LLWString wtext = utf8str_to_wstring(utf8text);
return getWidthF32(wtext.c_str(), begin_offset, max_chars);

View File

@ -135,12 +135,12 @@ public:
S32 getWidth(const std::string& utf8text) const;
S32 getWidth(const llwchar* wchars) const;
S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars ) const;
S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars) const;
S32 getWidth(const llwchar* wchars, S32 offset, S32 max_chars) const;
F32 getWidthF32(const std::string& utf8text) const;
F32 getWidthF32(const llwchar* wchars) const;
F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars ) const;
F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars) const;
F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars, bool no_padding = false) const;
// The following are called often, frequently with large buffers, so do not use a string interface

View File

@ -109,11 +109,12 @@ void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, c
}
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);
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));
gFloaterView->adjustToFitScreen(pHelperFloater, FALSE);
}
void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p)

View File

@ -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<LLPanelEmojiComplete> 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 (mEmojis.empty())
return;
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);
}
const size_t firstVisibleIdx = mScrollPos;
const size_t lastVisibleIdx = llmin(mScrollPos + mVisibleEmojis, mEmojis.size()) - 1;
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 (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();
mRenderRect = getLocalRect();
S32 ctrlWidth = mRenderRect.getWidth();
S32 ctrlHeight = mRenderRect.getHeight();
mEmojiWidth = mFont->getWidthF32(u8"\U0001F431") + mPadding * 2;
mVisibleEmojis = ctrlWidth / mEmojiWidth;
mRenderRect = getLocalRect().stretch((ctrlWidth - mVisibleEmojis * mEmojiWidth) / -2, 0);
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();
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()

View File

@ -41,6 +41,7 @@ public:
{
Optional<bool> autosize;
Optional<bool> noscroll;
Optional<bool> vertical;
Optional<S32> max_emoji,
padding;
@ -68,6 +69,8 @@ public:
size_t getEmojiCount() const { return mEmojis.size(); }
void setEmojis(const LLWString& emojis);
void setEmojiHint(const std::string& hint);
bool isAutoSize() const { return mAutoSize; }
U32 getMaxShortCodeWidth() const;
protected:
void onEmojisChanged();
@ -75,7 +78,6 @@ protected:
void select(size_t emoji_idx);
void selectNext();
void selectPrevious();
void setFont(const LLFontGL* fontp);
void updateConstraints();
void updateScrollPos();
@ -84,14 +86,17 @@ protected:
const bool mAutoSize = false;
const bool mNoScroll = false;
const LLFontGL* mFont;
U16 mEmojiWidth = 0;
const bool mVertical = false;
const size_t mMaxVisible = 0;
const S32 mPadding = 8;
LLRect mRenderRect;
const LLUIImagePtr mSelectedImage;
const LLFontGL* mIconFont;
const LLFontGL* mTextFont;
LLWString mEmojis;
LLRect mRenderRect;
U16 mEmojiWidth = 0;
U16 mEmojiHeight = 0;
size_t mVisibleEmojis = 0;
size_t mFirstVisible = 0;
size_t mScrollPos = 0;

View File

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
name="emoji_complete"
single_instance="true"
layout="topleft"
can_close="false"
can_dock="false"
can_drag_on_left="false"
@ -7,22 +10,21 @@
can_resize="false"
can_tear_off="false"
header_height="0"
layout="topleft"
legacy_header_height="0"
height="40"
single_instance="true"
width="240"
height="40"
>
<emoji_complete
autosize="true"
height="30"
name="emoji_complete_ctrl"
follows="top|left"
layout="topleft"
left="5"
autosize="true"
vertical="true"
max_emoji="7"
name="emoji_complete_ctrl"
top="5"
width="230"
height="30"
left="5"
top="5"
>
</emoji_complete>
</floater>

View File

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<emoji_complete
autosize="false"
font="EmojiHuge"
hover_image="ListItem_Over"
selected_image="ListItem_Select"
max_emoji="7"
padding="8"
selected_image="ListItem_Select"
>
</emoji_complete>