diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index 383e4c121c..61ad2b3564 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -339,8 +339,6 @@ S32 wchar_utf8_length(const llwchar wc)
{
if (wc < 0x80)
{
- // This case will also catch negative values which are
- // technically invalid.
return 1;
}
else if (wc < 0x800)
diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp
index b529ae2aba..1cd9dfe432 100644
--- a/indra/llrender/llfontfreetype.cpp
+++ b/indra/llrender/llfontfreetype.cpp
@@ -762,12 +762,25 @@ void LLFontFreetype::renderGlyph(EFontGlyphType bitmap_type, U32 glyph_index) co
load_flags |= FT_LOAD_COLOR;
}
- // try to load given glyph, if that fails, fallback to ?. This can happen with invalid unicode codepoints.
- if( 0 != FT_Load_Glyph(mFTFace, glyph_index, load_flags) )
- glyph_index = FT_Get_Char_Index( mFTFace, L'?');
- //
+ FT_Error error = FT_Load_Glyph(mFTFace, glyph_index, load_flags);
+ if (FT_Err_Ok != error)
+ {
+ std::string message = llformat(
+ "Error %d (%s) loading glyph %u: bitmap_type=%u, load_flags=%d",
+ error, FT_Error_String(error), glyph_index, bitmap_type, load_flags);
+ LL_WARNS_ONCE() << message << LL_ENDL;
+ error = FT_Load_Glyph(mFTFace, glyph_index, load_flags ^ FT_LOAD_COLOR);
- llassert_always(! FT_Load_Glyph(mFTFace, glyph_index, load_flags) );
+ // try to load given glyph, if that fails, fallback to ?. This can happen with invalid unicode codepoints.
+ if (FT_Err_Ok != error)
+ {
+ glyph_index = FT_Get_Char_Index( mFTFace, L'?');
+ error = FT_Load_Glyph(mFTFace, glyph_index, load_flags ^ FT_LOAD_COLOR);
+ }
+ //
+
+ llassert_always_msg(FT_Err_Ok == error, message.c_str());
+ }
llassert_always(! FT_Render_Glyph(mFTFace->glyph, gFontRenderMode) );
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp
index f963ae6ab0..d39c4eb834 100644
--- a/indra/llrender/llfontgl.cpp
+++ b/indra/llrender/llfontgl.cpp
@@ -446,7 +446,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons
S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const
{
- return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+ return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW);
}
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
@@ -456,12 +456,12 @@ S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y
S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const
{
- return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE, FALSE);
+ return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW);
}
S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow) const
{
- return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE, FALSE);
+ return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow);
}
// font metrics - override for LLFontFreetype that returns units of virtual pixels
diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h
index f5f0f29f62..13c9e8bfce 100644
--- a/indra/llrender/llfontgl.h
+++ b/indra/llrender/llfontgl.h
@@ -99,7 +99,7 @@ public:
S32 max_chars = S32_MAX,
F32* right_x=NULL,
BOOL use_ellipses = FALSE,
- BOOL use_color = FALSE) const;
+ BOOL use_color = TRUE) const;
S32 render(const LLWString &text, S32 begin_offset,
const LLRectf& rect,
@@ -109,7 +109,7 @@ public:
S32 max_chars = S32_MAX,
F32* right_x=NULL,
BOOL use_ellipses = FALSE,
- BOOL use_color = FALSE) const;
+ BOOL use_color = TRUE) const;
S32 render(const LLWString &text, S32 begin_offset,
F32 x, F32 y,
@@ -119,12 +119,12 @@ public:
S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX,
F32* right_x=NULL,
BOOL use_ellipses = FALSE,
- BOOL use_color = FALSE) const;
+ BOOL use_color = TRUE) const;
S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const;
// renderUTF8 does a conversion, so is slower!
- S32 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 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, S32 max_pixels = S32_MAX, F32* right_x = NULL, BOOL use_ellipses = FALSE, BOOL use_color = TRUE) const;
S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const;
S32 renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style = NORMAL, ShadowType shadow = NO_SHADOW) const;
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 45281bf17a..6c3b6c057c 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -70,7 +70,7 @@ LLButton::Params::Params()
label_shadow("label_shadow", true),
auto_resize("auto_resize", false),
use_ellipses("use_ellipses", false),
- use_font_color("use_font_color", false),
+ use_font_color("use_font_color", true),
image_unselected("image_unselected"),
image_selected("image_selected"),
image_hover_selected("image_hover_selected"),
diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp
index e29f3436cf..f16c38a11a 100644
--- a/indra/llui/llemojidictionary.cpp
+++ b/indra/llui/llemojidictionary.cpp
@@ -41,9 +41,9 @@
static const std::string SKINNED_EMOJI_FILENAME("emoji_characters.xml");
static const std::string SKINNED_CATEGORY_FILENAME("emoji_categories.xml");
static const std::string COMMON_GROUP_FILENAME("emoji_groups.xml");
-static const std::string GROUP_NAME_ALL("all");
-static const std::string GROUP_NAME_OTHERS("others");
static const std::string GROUP_NAME_SKIP("skip");
+// https://www.compart.com/en/unicode/U+1F302
+static const S32 GROUP_OTHERS_IMAGE_INDEX = 0x1F302;
// ============================================================================
// Helper functions
@@ -143,68 +143,76 @@ LLWString LLEmojiDictionary::findMatchingEmojis(const std::string& needle) const
return result;
}
-void LLEmojiDictionary::findByShortCode(std::vector& result, const std::string& needle) const
+// static
+bool LLEmojiDictionary::searchInShortCode(std::size_t& begin, std::size_t& end, const std::string& shortCode, const std::string& needle)
+{
+ begin = 0;
+ end = 1;
+ std::size_t index = 1;
+ // Search for begin
+ char d = tolower(needle[index++]);
+ while (end < shortCode.size())
+ {
+ char s = tolower(shortCode[end++]);
+ if (s == d)
+ {
+ begin = end - 1;
+ break;
+ }
+ }
+ if (!begin)
+ return false;
+ // Search for end
+ d = tolower(needle[index++]);
+ if (!d)
+ return true;
+ while (end < shortCode.size() && index <= needle.size())
+ {
+ char s = tolower(shortCode[end++]);
+ if (s == d)
+ {
+ if (index == needle.size())
+ return true;
+ d = tolower(needle[index++]);
+ continue;
+ }
+ switch (s)
+ {
+ case L'-':
+ case L'_':
+ case L'+':
+ continue;
+ }
+ break;
+ }
+ return false;
+}
+
+void LLEmojiDictionary::findByShortCode(
+ std::vector& result,
+ const std::string& needle
+) const
{
result.clear();
if (needle.empty() || needle.front() != ':')
return;
- auto search = [needle](std::size_t& begin, std::size_t& end, const std::string& shortCode) -> bool
- {
- begin = 0;
- end = 1;
- std::size_t index = 1;
- // Search for begin
- char d = tolower(needle[index++]);
- while (end < shortCode.size())
- {
- char s = tolower(shortCode[end++]);
- if (s == d)
- {
- begin = end - 1;
- break;
- }
- }
- if (!begin)
- return false;
- // Search for end
- d = tolower(needle[index++]);
- while (end < shortCode.size() && index <= needle.size())
- {
- char s = tolower(shortCode[end++]);
- if (s == d)
- {
- if (index == needle.size())
- return true;
- d = tolower(needle[index++]);
- continue;
- }
- switch (s)
- {
- case L'-':
- case L'_':
- case L'+':
- continue;
- }
- break;
- }
- return false;
- };
-
- std::map> results;
+ std::map> results;
for (const LLEmojiDescriptor& d : mEmojis)
{
- if (d.ShortCodes.empty())
- continue;
- const std::string& shortCode = d.ShortCodes.front();
- if (shortCode.size() < needle.size() || shortCode.front() != needle.front())
- continue;
- std::size_t begin, end;
- if (search(begin, end, shortCode))
+ if (!d.ShortCodes.empty())
{
- results[begin].emplace_back(d.Character, shortCode, begin, end);
+ const std::string& shortCode = d.ShortCodes.front();
+ if (shortCode.size() >= needle.size() && shortCode.front() == needle.front())
+ {
+ std::size_t begin, end;
+ if (searchInShortCode(begin, end, shortCode, needle))
+ {
+ results[begin].emplace_back(d.Character, shortCode, begin, end);
+ }
+ }
}
}
@@ -312,27 +320,13 @@ void LLEmojiDictionary::loadGroups()
}
mGroups.clear();
- // Add group "all"
- mGroups.emplace_back();
- // https://www.compart.com/en/unicode/U+1F50D
- mGroups.front().Character = 0x1F50D;
- // https://www.compart.com/en/unicode/U+1F302
- llwchar iconOthers = 0x1F302;
// Register all groups
for (LLSD::array_const_iterator it = data.beginArray(), end = data.endArray(); it != end; ++it)
{
const LLSD& sd = *it;
const std::string& name = sd["Name"].asStringRef();
- if (name == GROUP_NAME_ALL)
- {
- mGroups.front().Character = loadIcon(sd);
- }
- else if (name == GROUP_NAME_OTHERS)
- {
- iconOthers = loadIcon(sd);
- }
- else if (name == GROUP_NAME_SKIP)
+ if (name == GROUP_NAME_SKIP)
{
mSkipCategories = loadCategories(sd);
translateCategories(mSkipCategories);
@@ -355,7 +349,7 @@ void LLEmojiDictionary::loadGroups()
// Add group "others"
mGroups.emplace_back();
- mGroups.back().Character = iconOthers;
+ mGroups.back().Character = GROUP_OTHERS_IMAGE_INDEX;
}
void LLEmojiDictionary::loadEmojis()
diff --git a/indra/llui/llemojidictionary.h b/indra/llui/llemojidictionary.h
index 66b564b70a..4af376df64 100644
--- a/indra/llui/llemojidictionary.h
+++ b/indra/llui/llemojidictionary.h
@@ -89,6 +89,7 @@ public:
static void initClass();
LLWString findMatchingEmojis(const std::string& needle) const;
+ static bool searchInShortCode(std::size_t& begin, std::size_t& end, const std::string& shortCode, const std::string& needle);
void findByShortCode(std::vector& result, const std::string& needle) const;
const LLEmojiDescriptor* getDescriptorFromEmoji(llwchar emoji) const;
const LLEmojiDescriptor* getDescriptorFromShortCode(const std::string& short_code) const;
diff --git a/indra/llui/llemojihelper.cpp b/indra/llui/llemojihelper.cpp
index 9411f7cac5..89e6ddf987 100644
--- a/indra/llui/llemojihelper.cpp
+++ b/indra/llui/llemojihelper.cpp
@@ -36,7 +36,7 @@
// Constants
//
-constexpr char DEFAULT_EMOJI_HELPER_FLOATER[] = "emoji_complete";
+constexpr char DEFAULT_EMOJI_HELPER_FLOATER[] = "emoji_picker";
constexpr S32 HELPER_FLOATER_OFFSET_X = 0;
constexpr S32 HELPER_FLOATER_OFFSET_Y = 0;
@@ -117,9 +117,10 @@ void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, c
pHelperFloater->openFloater(LLSD().with("hint", short_code));
}
-void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p)
+void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p, bool strict)
{
- if (ctrl_p && !isActive(ctrl_p))
+ mIsHideDisabled &= !strict;
+ if (mIsHideDisabled || (ctrl_p && !isActive(ctrl_p)))
{
return;
}
diff --git a/indra/llui/llemojihelper.h b/indra/llui/llemojihelper.h
index 58f68d12a4..e826ff93e6 100644
--- a/indra/llui/llemojihelper.h
+++ b/indra/llui/llemojihelper.h
@@ -45,7 +45,8 @@ public:
bool isActive(const LLUICtrl* ctrl_p) const;
static bool isCursorInEmojiCode(const LLWString& wtext, S32 cursor_pos, S32* short_code_pos_p = nullptr);
void showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function commit_cb);
- void hideHelper(const LLUICtrl* ctrl_p = nullptr);
+ void hideHelper(const LLUICtrl* ctrl_p = nullptr, bool strict = false);
+ void setIsHideDisabled(bool disabled) { mIsHideDisabled = disabled; };
// Eventing
bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask);
@@ -61,4 +62,5 @@ private:
boost::signals2::connection mHostCtrlFocusLostConn;
boost::signals2::connection mHelperCommitConn;
std::function mEmojiCommitCb;
+ bool mIsHideDisabled;
};
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index d86deb63cf..dbd59e3393 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -194,6 +194,7 @@ LLFloater::Params::Params()
save_visibility("save_visibility", false),
can_dock("can_dock", false),
show_title("show_title", true),
+ auto_close("auto_close", false),
positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE),
header_height("header_height", 0),
legacy_header_height("legacy_header_height", 0),
@@ -271,6 +272,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
mCanSnooze(p.can_snooze), // FIRE-11724: Snooze group chat
mDragOnLeft(p.can_drag_on_left),
mResizable(p.can_resize),
+ mAutoClose(p.auto_close),
mPositioning(p.positioning),
mMinWidth(p.min_width),
mMinHeight(p.min_height),
@@ -539,6 +541,7 @@ void LLFloater::enableResizeCtrls(bool enable, bool width, bool height)
void LLFloater::destroy()
{
+ gFloaterView->onDestroyFloater(this);
// LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before
// it was deleted via LLMortician::updateClass(). See EXT-8458.
LLFloaterReg::removeInstance(mInstanceName, mKey);
@@ -737,7 +740,7 @@ void LLFloater::openFloater(const LLSD& key)
if (getHost() != NULL)
{
getHost()->setMinimized(FALSE);
- getHost()->setVisibleAndFrontmost(mAutoFocus);
+ getHost()->setVisibleAndFrontmost(mAutoFocus && !getIsChrome());
getHost()->showFloater(this);
// Make sure the floater knows it's not torn off
mTornOff = false;
@@ -754,7 +757,7 @@ void LLFloater::openFloater(const LLSD& key)
}
applyControlsAndPosition(floater_to_stack);
setMinimized(FALSE);
- setVisibleAndFrontmost(mAutoFocus);
+ setVisibleAndFrontmost(mAutoFocus && !getIsChrome());
}
mOpenSignal(this, key);
@@ -1638,7 +1641,7 @@ void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition, BOOL r
if (floaterp->isFrontmost())
{
// make sure to bring self and sibling floaters to front
- gFloaterView->bringToFront(floaterp);
+ gFloaterView->bringToFront(floaterp, floaterp->getAutoFocus() && !getIsChrome());
}
}
@@ -1820,6 +1823,7 @@ BOOL LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)
return was_minimized || LLPanel::handleDoubleClick(x, y, mask);
}
+// virtual
void LLFloater::bringToFront( S32 x, S32 y )
{
if (getVisible() && pointInView(x, y))
@@ -1834,12 +1838,20 @@ void LLFloater::bringToFront( S32 x, S32 y )
LLFloaterView* parent = dynamic_cast( getParent() );
if (parent)
{
- parent->bringToFront( this );
+ parent->bringToFront(this, !getIsChrome());
}
}
}
}
+// virtual
+void LLFloater::goneFromFront()
+{
+ if (mAutoClose)
+ {
+ closeFloater();
+ }
+}
// virtual
void LLFloater::setVisibleAndFrontmost(BOOL take_focus,const LLSD& key)
@@ -2619,7 +2631,8 @@ LLFloaterView::LLFloaterView (const Params& p)
mSnapOffsetBottom(0),
mSnapOffsetChatBar(0),
mSnapOffsetLeft(0),
- mSnapOffsetRight(0)
+ mSnapOffsetRight(0),
+ mFrontChild(NULL)
{
mSnapView = getHandle();
}
@@ -2775,7 +2788,7 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus, BOOL restore
if (!child)
return;
- if (mFrontChildHandle.get() == child)
+ if (mFrontChild == child)
{
if (give_focus && child->canFocusStealFrontmost() && !gFocusMgr.childHasKeyboardFocus(child))
{
@@ -2784,7 +2797,12 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus, BOOL restore
return;
}
- mFrontChildHandle = child->getHandle();
+ if (mFrontChild && !mFrontChild->isDead())
+ {
+ mFrontChild->goneFromFront();
+ }
+
+ mFrontChild = child;
// *TODO: make this respect floater's mAutoFocus value, instead of
// using parameter
@@ -3371,6 +3389,9 @@ LLFloater *LLFloaterView::getBackmost() const
void LLFloaterView::syncFloaterTabOrder()
{
+ if (mFrontChild && !mFrontChild->isDead() && mFrontChild->getIsChrome())
+ return;
+
// look for a visible modal dialog, starting from first
LLModalDialog* modal_dialog = NULL;
for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
@@ -3406,11 +3427,11 @@ void LLFloaterView::syncFloaterTabOrder()
LLFloater* floaterp = dynamic_cast(*child_it);
if (gFocusMgr.childHasKeyboardFocus(floaterp))
{
- if (mFrontChildHandle.get() != floaterp)
+ if (mFrontChild != floaterp)
{
// Grab a list of the top floaters that want to stay on top of the focused floater
std::list listTop;
- if (mFrontChildHandle.get() && !mFrontChildHandle.get()->canFocusStealFrontmost())
+ if (mFrontChild && !mFrontChild->canFocusStealFrontmost())
{
for (LLView* childp : *getChildList())
{
@@ -3430,7 +3451,7 @@ void LLFloaterView::syncFloaterTabOrder()
{
sendChildToFront(childp);
}
- mFrontChildHandle = listTop.back()->getHandle();
+ mFrontChild = listTop.back();
}
}
@@ -3526,6 +3547,14 @@ void LLFloaterView::setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LL
}
}
+void LLFloaterView::onDestroyFloater(LLFloater* floater)
+{
+ if (mFrontChild == floater)
+ {
+ mFrontChild = nullptr;
+ }
+}
+
// Prevent floaters being dragged under main chat bar
void LLFloaterView::setMainChatbarRect(LLLayoutPanel* panel, const LLRect& chatbar_rect)
{
@@ -3650,6 +3679,7 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
mDefaultRelativeY = p.rel_y;
mPositioning = p.positioning;
+ mAutoClose = p.auto_close;
mHostedFloaterShowtitlebar = p.hosted_floater_show_titlebar; // MultiFloater without titlebar for hosted floater
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 0ee96b314d..21b132f239 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -168,7 +168,8 @@ public:
save_visibility,
save_dock_state,
can_dock,
- show_title;
+ show_title,
+ auto_close;
Optional positioning;
@@ -411,6 +412,7 @@ protected:
void setInstanceName(const std::string& name);
virtual void bringToFront(S32 x, S32 y);
+ virtual void goneFromFront();
void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized
const LLRect& getExpandedRect() const { return mExpandedRect; }
@@ -515,6 +517,7 @@ private:
bool mFocusStealsFrontmost = true; // FALSE if we don't want the currently focused floater to cover this floater without user interaction
BOOL mDragOnLeft;
BOOL mResizable;
+ BOOL mAutoClose;
BOOL mCanSnooze; // FIRE-11724: Snooze group chat
LLFloaterEnums::EOpenPositioning mPositioning;
@@ -642,6 +645,7 @@ public:
// Fix for bad edge snapping
void setToolbarRect(LLToolBarEnums::EToolBarLocation tb, const LLRect& toolbar_rect);
+ void onDestroyFloater(LLFloater* floater);
// Prevent floaters being dragged under main chat bar
void setMainChatbarRect(LLLayoutPanel* panel, const LLRect& chatbar_rect);
@@ -666,7 +670,7 @@ private:
S32 mMinimizePositionVOffset;
typedef std::vector, boost::signals2::connection> > hidden_floaters_t;
hidden_floaters_t mHiddenFloaters;
- LLHandle mFrontChildHandle;
+ LLFloater * mFrontChild;
// Prevent floaters being dragged under main chat bar
LLRect mMainChatbarRect;
diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp
index 4a0e05930f..9606d6e7ca 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -959,7 +959,7 @@ void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y
//
font->renderUTF8(mLabel, 0, x, y, color,
LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, /*use_ellipses*/TRUE, /*use_color*/FALSE);
+ S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, /*use_ellipses*/TRUE);
}
void LLFolderViewItem::draw()
@@ -1090,7 +1090,7 @@ void LLFolderViewItem::draw()
{
suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,
LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- S32_MAX, S32_MAX, &right_x, /*use_ellipses*/FALSE, /*use_color*/FALSE );
+ S32_MAX, S32_MAX, &right_x);
}
//--------------------------------------------------------------------------------//
@@ -1104,7 +1104,7 @@ void LLFolderViewItem::draw()
F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
font->renderUTF8(combined_string, filter_offset, match_string_left, yy,
sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- filter_string_length, S32_MAX, &right_x, FALSE, FALSE);
+ filter_string_length, S32_MAX, &right_x);
}
else
{
@@ -1115,7 +1115,7 @@ void LLFolderViewItem::draw()
F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
font->renderUTF8(mLabel, filter_offset, match_string_left, yy,
sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- label_filter_length, S32_MAX, &right_x, FALSE, FALSE);
+ label_filter_length, S32_MAX, &right_x);
}
S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length;
@@ -1126,7 +1126,7 @@ void LLFolderViewItem::draw()
F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
suffix_font->renderUTF8(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor,
LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- suffix_filter_length, S32_MAX, &right_x, FALSE, FALSE);
+ suffix_filter_length, S32_MAX, &right_x);
}
}
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index b8f13b166d..c8f4704c71 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -2190,7 +2190,7 @@ void LLLineEditor::draw()
LLFontGL::NO_SHADOW,
S32_MAX,
mTextRightEdge - ll_round(rendered_pixels_right),
- &rendered_pixels_right, FALSE);
+ &rendered_pixels_right);
}
// Draw children (border)
LLView::draw();
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 5cdcb7825e..774df5b943 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -187,7 +187,7 @@ LLTextBase::Params::Params()
trusted_content("trusted_content", true),
always_show_icons("always_show_icons", false),
use_ellipses("use_ellipses", false),
- use_color("use_color", false),
+ use_color("use_color", true),
// Optional icon position
icon_positioning("icon_positioning", LLTextBaseEnums::RIGHT),
// Optional icon position
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 207b2c3c27..b9e138ac5f 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -558,7 +558,8 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out,
}
}
-void LLTextEditor::setShowEmojiHelper(bool show) {
+void LLTextEditor::setShowEmojiHelper(bool show)
+{
if (!mShowEmojiHelper)
{
LLEmojiHelper::instance().hideHelper(this);
@@ -1282,6 +1283,16 @@ void LLTextEditor::addChar(llwchar wc)
}
}
+void LLTextEditor::showEmojiHelper()
+{
+ if (mReadOnly || !mShowEmojiHelper)
+ return;
+
+ const LLRect cursorRect(getLocalRectFromDocIndex(mCursorPos));
+ auto cb = [this](llwchar emoji) { insertEmoji(emoji); };
+ LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb);
+}
+
void LLTextEditor::tryToShowEmojiHelper()
{
if (mReadOnly || !mShowEmojiHelper)
@@ -1297,6 +1308,10 @@ void LLTextEditor::tryToShowEmojiHelper()
auto cb = [this](llwchar emoji) { handleEmojiCommit(emoji); };
LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, part, cb);
}
+ else
+ {
+ LLEmojiHelper::instance().hideHelper();
+ }
}
void LLTextEditor::addLineBreakChar(BOOL group_together)
@@ -2096,7 +2111,12 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
// Handle most keys only if the text editor is writeable.
if( !mReadOnly )
{
- if( mAutoIndent && '}' == uni_char )
+ if (mShowEmojiHelper && uni_char < 0x80 && LLEmojiHelper::instance().handleKey(this, (KEY)uni_char, MASK_NONE))
+ {
+ return TRUE;
+ }
+
+ if( mAutoIndent && '}' == uni_char )
{
unindentLineBeforeCloseBrace();
}
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index fae0a057ca..aad0d2a864 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -216,6 +216,7 @@ public:
void setShowContextMenu(bool show) { mShowContextMenu = show; }
bool getShowContextMenu() const { return mShowContextMenu; }
+ void showEmojiHelper();
void setShowEmojiHelper(bool show);
bool getShowEmojiHelper() const { return mShowEmojiHelper; }
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 5d012dad83..77ce32d15d 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -798,25 +798,20 @@ void LLUICtrl::setIsChrome(BOOL is_chrome)
// virtual
BOOL LLUICtrl::getIsChrome() const
-{
+{
+ if (mIsChrome)
+ return TRUE;
+
LLView* parent_ctrl = getParent();
- while(parent_ctrl)
+ while (parent_ctrl)
{
- if(parent_ctrl->isCtrl())
- {
- break;
- }
+ if (parent_ctrl->isCtrl())
+ return ((LLUICtrl*)parent_ctrl)->getIsChrome();
+
parent_ctrl = parent_ctrl->getParent();
}
-
- if(parent_ctrl)
- {
- return mIsChrome || ((LLUICtrl*)parent_ctrl)->getIsChrome();
- }
- else
- {
- return mIsChrome ;
- }
+
+ return FALSE;
}
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 80b37016a3..93352da968 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -1392,8 +1392,7 @@ void LLView::drawDebugRect()
std::string debug_text = llformat("%s (%d x %d)", getName().c_str(),
debug_rect.getWidth(), debug_rect.getHeight());
LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,
- LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);
+ LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
}
}
LLUI::popMatrix();
diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm
index cd04a6122d..b62df21803 100644
--- a/indra/llwindow/llopenglview-objc.mm
+++ b/indra/llwindow/llopenglview-objc.mm
@@ -746,33 +746,49 @@ attributedStringInfo getSegments(NSAttributedString *str)
// SL-19801 Special workaround for system emoji picker
if ([aString length] == 2)
{
- uint32_t b0 = [aString characterAtIndex:0];
- uint32_t b1 = [aString characterAtIndex:1];
- if (((b0 & 0xF000) == 0xD000) && ((b1 & 0xF000) == 0xD000))
- {
- uint32_t b = 0x10000 | ((b0 & 0x3F) << 10) | (b1 & 0x3FF);
- callUnicodeCallback(b, 0);
- return;
- }
- }
-
- if (!mHasMarkedText)
- {
- for (NSInteger i = 0; i < [aString length]; i++)
- {
- callUnicodeCallback([aString characterAtIndex:i], mModifiers);
- }
- } else {
- resetPreedit();
- // We may never get this point since unmarkText may be called before insertText ever gets called once we submit our text.
- // But just in case...
-
- for (NSInteger i = 0; i < [aString length]; i++)
- {
- handleUnicodeCharacter([aString characterAtIndex:i]);
- }
- mHasMarkedText = FALSE;
+ @try
+ {
+ uint32_t b0 = [aString characterAtIndex:0];
+ uint32_t b1 = [aString characterAtIndex:1];
+ if (((b0 & 0xF000) == 0xD000) && ((b1 & 0xF000) == 0xD000))
+ {
+ uint32_t b = 0x10000 | ((b0 & 0x3F) << 10) | (b1 & 0x3FF);
+ callUnicodeCallback(b, 0);
+ return;
+ }
+ }
+ @catch(NSException * e)
+ {
+ // One of the characters is an attribute string?
+ NSLog(@"Encountered an unsupported attributed character. Exception: %@ String: %@", e.name, aString);
+ return;
+ }
}
+
+ @try
+ {
+ if (!mHasMarkedText)
+ {
+ for (NSInteger i = 0; i < [aString length]; i++)
+ {
+ callUnicodeCallback([aString characterAtIndex:i], mModifiers);
+ }
+ } else {
+ resetPreedit();
+ // We may never get this point since unmarkText may be called before insertText ever gets called once we submit our text.
+ // But just in case...
+
+ for (NSInteger i = 0; i < [aString length]; i++)
+ {
+ handleUnicodeCharacter([aString characterAtIndex:i]);
+ }
+ mHasMarkedText = FALSE;
+ }
+ }
+ @catch(NSException * e)
+ {
+ NSLog(@"Failed to process an attributed string. Exception: %@ String: %@", e.name, aString);
+ }
}
- (void) insertNewline:(id)sender
diff --git a/indra/newview/fscommon.cpp b/indra/newview/fscommon.cpp
index cde363dbf1..b4421c4ae9 100644
--- a/indra/newview/fscommon.cpp
+++ b/indra/newview/fscommon.cpp
@@ -536,8 +536,8 @@ void FSCommon::updateUsedEmojis(LLWString text)
}
}
- if (emojiSent)
- {
- LLFloaterEmojiPicker::onRecentlyUsedChanged();
- }
+ if (!emojiSent)
+ return;
+
+ LLFloaterEmojiPicker::saveState();
}
\ No newline at end of file
diff --git a/indra/newview/fsfloaterim.cpp b/indra/newview/fsfloaterim.cpp
index 3a04ac3416..e486bb31f1 100644
--- a/indra/newview/fsfloaterim.cpp
+++ b/indra/newview/fsfloaterim.cpp
@@ -959,7 +959,7 @@ BOOL FSFloaterIM::postBuild()
mInputEditor->setCommitCallback(boost::bind(&FSFloaterIM::sendMsgFromInputEditor, this, CHAT_TYPE_NORMAL));
mEmojiRecentPanelToggleBtn = getChild("emoji_recent_panel_toggle_btn");
- mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(this); });
+ mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(); });
mEmojiRecentPanel = getChild("emoji_recent_layout_panel");
mEmojiRecentPanel->setVisible(false);
@@ -974,9 +974,9 @@ BOOL FSFloaterIM::postBuild()
mEmojiPickerToggleBtn = getChild("emoji_picker_toggle_btn");
mEmojiPickerToggleBtn->setLabel(LLUIString(LLWString(1, 128512)));
- mEmojiPickerToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerToggleBtnClicked(this); });
+ mEmojiPickerToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerToggleBtnClicked(); });
- mRecentEmojisUpdatedCallbackConnection = LLFloaterEmojiPicker::setRecentEmojisUpdatedCallback([this](const std::list& recent_emojis_list) { initEmojiRecentPanel(false); });
+ mRecentEmojisUpdatedCallbackConnection = LLFloaterEmojiPicker::setRecentEmojisUpdatedCallback([this](const std::list& recent_emojis_list) { initEmojiRecentPanel(); });
getChild("send_chat")->setCommitCallback(boost::bind(&FSFloaterIM::sendMsgFromInputEditor, this, CHAT_TYPE_NORMAL));
getChild("chat_search_btn")->setCommitCallback(boost::bind(&FSFloaterIM::onChatSearchButtonClicked, this));
@@ -2472,26 +2472,19 @@ bool FSFloaterIM::applyRectControl()
return res;
}
-void FSFloaterIM::onEmojiRecentPanelToggleBtnClicked(FSFloaterIM* self)
+void FSFloaterIM::onEmojiRecentPanelToggleBtnClicked()
{
- bool show = !self->mEmojiRecentPanel->getVisible();
- bool restore_focus = !show || (gFocusMgr.getLastKeyboardFocus() == self->mInputEditor);
-
+ BOOL show = mEmojiRecentPanel->getVisible() ? FALSE : TRUE;
if (show)
{
- self->initEmojiRecentPanel(!restore_focus);
+ initEmojiRecentPanel();
}
- self->mEmojiRecentPanel->setVisible(show ? TRUE : FALSE);
- self->mEmojiRecentPanelToggleBtn->setImageOverlay(show ? "Arrow_Up" : "Arrow_Down");
-
- if (restore_focus)
- {
- self->mInputEditor->setFocus(true);
- }
+ mEmojiRecentPanel->setVisible(show);
+ mInputEditor->setFocus(TRUE);
}
-void FSFloaterIM::initEmojiRecentPanel(bool moveFocus)
+void FSFloaterIM::initEmojiRecentPanel()
{
std::list& recentlyUsed = LLFloaterEmojiPicker::getRecentlyUsed();
if (recentlyUsed.empty())
@@ -2509,10 +2502,6 @@ void FSFloaterIM::initEmojiRecentPanel(bool moveFocus)
mEmojiRecentIconsCtrl->setEmojis(emojis);
mEmojiRecentEmptyText->setVisible(FALSE);
mEmojiRecentIconsCtrl->setVisible(TRUE);
- if (moveFocus)
- {
- mEmojiRecentIconsCtrl->setFocus(TRUE);
- }
}
}
@@ -2525,44 +2514,12 @@ void FSFloaterIM::onRecentEmojiPicked(const LLSD& value)
if (wstr.size())
{
llwchar emoji = wstr[0];
- onEmojiPicked(emoji);
+ mInputEditor->insertEmoji(emoji);
}
}
}
-void FSFloaterIM::onEmojiPickerToggleBtnClicked(FSFloaterIM* self)
+void FSFloaterIM::onEmojiPickerToggleBtnClicked()
{
- if (LLFloaterEmojiPicker* picker = LLFloaterEmojiPicker::getInstance())
- {
- if (!picker->isShown() || (picker->isDependent() && picker->getDependee() && picker->getDependee() != self && picker->getDependee() != self->getHost()))
- {
- picker->show(
- [self](llwchar emoji) { self->onEmojiPicked(emoji); },
- [self]() { self->onEmojiPickerClosed(); });
-
- if (self->getHost())
- {
- self->getHost()->addDependentFloater(picker, TRUE, TRUE);
- }
- else
- {
- self->addDependentFloater(picker, TRUE, TRUE);
- }
- }
- else
- {
- picker->closeFloater();
- }
- }
-}
-
-void FSFloaterIM::onEmojiPicked(llwchar emoji)
-{
- mInputEditor->insertEmoji(emoji);
- mInputEditor->setFocus(TRUE);
-}
-
-void FSFloaterIM::onEmojiPickerClosed()
-{
- mInputEditor->setFocus(TRUE);
+ mInputEditor->showEmojiHelper();
}
diff --git a/indra/newview/fsfloaterim.h b/indra/newview/fsfloaterim.h
index 4aa5b0cc92..ac42d03cd7 100644
--- a/indra/newview/fsfloaterim.h
+++ b/indra/newview/fsfloaterim.h
@@ -161,7 +161,7 @@ public:
void timedUpdate();
- static void onEmojiPickerToggleBtnClicked(FSFloaterIM* self);
+ void onEmojiPickerToggleBtnClicked();
protected:
/* virtual */
@@ -241,11 +241,9 @@ private:
bool onChatOptionsVisibleContextMenuItem(const LLSD& userdata);
bool onChatOptionsEnableContextMenuItem(const LLSD& userdata);
- static void onEmojiRecentPanelToggleBtnClicked(FSFloaterIM* self);
- void initEmojiRecentPanel(bool moveFocus);
+ void onEmojiRecentPanelToggleBtnClicked();
+ void initEmojiRecentPanel();
void onRecentEmojiPicked(const LLSD& value);
- void onEmojiPicked(llwchar emoji);
- void onEmojiPickerClosed();
FSPanelChatControlPanel* mControlPanel;
LLUUID mSessionID;
diff --git a/indra/newview/fsfloaterimcontainer.cpp b/indra/newview/fsfloaterimcontainer.cpp
index 29fdefe8e3..2192b8dfe9 100644
--- a/indra/newview/fsfloaterimcontainer.cpp
+++ b/indra/newview/fsfloaterimcontainer.cpp
@@ -37,7 +37,7 @@
#include "llfloaterreg.h"
#include "llchiclet.h"
#include "llchicletbar.h"
-#include "llfloateremojipicker.h"
+#include "llemojihelper.h"
#include "lltoolbarview.h"
#include "llvoiceclient.h"
@@ -583,8 +583,6 @@ void FSFloaterIMContainer::sessionIDUpdated(const LLUUID& old_session_id, const
void FSFloaterIMContainer::tabOpen(LLFloater* opened_floater, bool from_click)
{
- auto picker = LLFloaterEmojiPicker::getInstance();
- if (picker && picker->isShown() && picker->isDependent() && (picker->getDependee() == this || picker->getDependee() == opened_floater))
- picker->closeFloater();
+ LLEmojiHelper::instance().hideHelper(nullptr, true);
}
// EOF
diff --git a/indra/newview/fsfloaternearbychat.cpp b/indra/newview/fsfloaternearbychat.cpp
index b27fc5344a..ed95e017eb 100644
--- a/indra/newview/fsfloaternearbychat.cpp
+++ b/indra/newview/fsfloaternearbychat.cpp
@@ -148,7 +148,7 @@ BOOL FSFloaterNearbyChat::postBuild()
mInputEditorPad = mChatLayoutPanelHeight - mInputEditor->getRect().getHeight();
mEmojiRecentPanelToggleBtn = getChild("emoji_recent_panel_toggle_btn");
- mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(this); });
+ mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(); });
mEmojiRecentPanel = getChild("emoji_recent_layout_panel");
mEmojiRecentPanel->setVisible(false);
@@ -163,9 +163,9 @@ BOOL FSFloaterNearbyChat::postBuild()
mEmojiPickerToggleBtn = getChild("emoji_picker_toggle_btn");
mEmojiPickerToggleBtn->setLabel(LLUIString(LLWString(1, 128512)));
- mEmojiPickerToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerToggleBtnClicked(this); });
+ mEmojiPickerToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerToggleBtnClicked(); });
- mRecentEmojisUpdatedCallbackConnection = LLFloaterEmojiPicker::setRecentEmojisUpdatedCallback([this](const std::list& recent_emojis_list) { initEmojiRecentPanel(false); });
+ mRecentEmojisUpdatedCallbackConnection = LLFloaterEmojiPicker::setRecentEmojisUpdatedCallback([this](const std::list& recent_emojis_list) { initEmojiRecentPanel(); });
getChild("chat_history_btn")->setCommitCallback(boost::bind(&FSFloaterNearbyChat::onHistoryButtonClicked, this));
getChild("chat_search_btn")->setCommitCallback(boost::bind(&FSFloaterNearbyChat::onSearchButtonClicked, this));
@@ -970,26 +970,19 @@ void FSFloaterNearbyChat::handleMinimized(bool minimized)
}
}
-void FSFloaterNearbyChat::onEmojiRecentPanelToggleBtnClicked(FSFloaterNearbyChat* self)
+void FSFloaterNearbyChat::onEmojiRecentPanelToggleBtnClicked()
{
- bool show = !self->mEmojiRecentPanel->getVisible();
- bool restore_focus = !show || (gFocusMgr.getLastKeyboardFocus() == self->mInputEditor);
-
+ BOOL show = mEmojiRecentPanel->getVisible() ? FALSE : TRUE;
if (show)
{
- self->initEmojiRecentPanel(!restore_focus);
+ initEmojiRecentPanel();
}
- self->mEmojiRecentPanel->setVisible(show ? TRUE : FALSE);
- self->mEmojiRecentPanelToggleBtn->setImageOverlay(show ? "Arrow_Up" : "Arrow_Down");
-
- if (restore_focus)
- {
- self->mInputEditor->setFocus(true);
- }
+ mEmojiRecentPanel->setVisible(show);
+ mInputEditor->setFocus(TRUE);
}
-void FSFloaterNearbyChat::initEmojiRecentPanel(bool moveFocus)
+void FSFloaterNearbyChat::initEmojiRecentPanel()
{
std::list& recentlyUsed = LLFloaterEmojiPicker::getRecentlyUsed();
if (recentlyUsed.empty())
@@ -1007,10 +1000,6 @@ void FSFloaterNearbyChat::initEmojiRecentPanel(bool moveFocus)
mEmojiRecentIconsCtrl->setEmojis(emojis);
mEmojiRecentEmptyText->setVisible(FALSE);
mEmojiRecentIconsCtrl->setVisible(TRUE);
- if (moveFocus)
- {
- mEmojiRecentIconsCtrl->setFocus(TRUE);
- }
}
}
@@ -1023,44 +1012,12 @@ void FSFloaterNearbyChat::onRecentEmojiPicked(const LLSD& value)
if (wstr.size())
{
llwchar emoji = wstr[0];
- onEmojiPicked(emoji);
+ mInputEditor->insertEmoji(emoji);
}
}
}
-void FSFloaterNearbyChat::onEmojiPickerToggleBtnClicked(FSFloaterNearbyChat* self)
+void FSFloaterNearbyChat::onEmojiPickerToggleBtnClicked()
{
- if (LLFloaterEmojiPicker* picker = LLFloaterEmojiPicker::getInstance())
- {
- if (!picker->isShown() || (picker->isDependent() && picker->getDependee() && picker->getDependee() != self && picker->getDependee() != self->getHost()))
- {
- picker->show(
- [self](llwchar emoji) { self->onEmojiPicked(emoji); },
- [self]() { self->onEmojiPickerClosed(); });
-
- if (self->getHost())
- {
- self->getHost()->addDependentFloater(picker, TRUE, TRUE);
- }
- else
- {
- self->addDependentFloater(picker, TRUE, TRUE);
- }
- }
- else
- {
- picker->closeFloater();
- }
- }
-}
-
-void FSFloaterNearbyChat::onEmojiPicked(llwchar emoji)
-{
- mInputEditor->insertEmoji(emoji);
- mInputEditor->setFocus(TRUE);
-}
-
-void FSFloaterNearbyChat::onEmojiPickerClosed()
-{
- mInputEditor->setFocus(TRUE);
+ mInputEditor->showEmojiHelper();
}
diff --git a/indra/newview/fsfloaternearbychat.h b/indra/newview/fsfloaternearbychat.h
index 7c077d73c1..02acc7dd3f 100644
--- a/indra/newview/fsfloaternearbychat.h
+++ b/indra/newview/fsfloaternearbychat.h
@@ -101,7 +101,7 @@ public:
void handleMinimized(bool minimized);
- static void onEmojiPickerToggleBtnClicked(FSFloaterNearbyChat* self);
+ void onEmojiPickerToggleBtnClicked();
protected:
void onChatBoxKeystroke();
@@ -123,11 +123,9 @@ private:
bool onChatOptionsVisibleContextMenuItem(const LLSD& userdata);
bool onChatOptionsEnableContextMenuItem(const LLSD& userdata);
- static void onEmojiRecentPanelToggleBtnClicked(FSFloaterNearbyChat* self);
- void initEmojiRecentPanel(bool moveFocus);
+ void onEmojiRecentPanelToggleBtnClicked();
+ void initEmojiRecentPanel();
void onRecentEmojiPicked(const LLSD& value);
- void onEmojiPicked(llwchar emoji);
- void onEmojiPickerClosed();
FSChatHistory* mChatHistory;
FSChatHistory* mChatHistoryMuted;
diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp
index 9ac26821ae..65265ecd44 100644
--- a/indra/newview/llfloateremojipicker.cpp
+++ b/indra/newview/llfloateremojipicker.cpp
@@ -31,7 +31,7 @@
#include "llbutton.h"
#include "llcombobox.h"
#include "llemojidictionary.h"
-#include "llfiltereditor.h"
+#include "llemojihelper.h"
#include "llfloaterreg.h"
#include "llkeyboard.h"
#include "llscrollcontainer.h"
@@ -48,16 +48,25 @@ namespace {
// The following variables and constants are used for storing the floater state
// between different lifecycles of the floater and different sissions of the viewer
+// Floater constants
+static const S32 ALL_EMOJIS_GROUP_INDEX = -2;
+// https://www.compart.com/en/unicode/U+1F50D
+static const S32 ALL_EMOJIS_IMAGE_INDEX = 0x1F50D;
+static const S32 USED_EMOJIS_GROUP_INDEX = -1;
+// https://www.compart.com/en/unicode/U+23F2
+static const S32 USED_EMOJIS_IMAGE_INDEX = 0x23F2;
+// https://www.compart.com/en/unicode/U+1F6D1
+static const S32 EMPTY_LIST_IMAGE_INDEX = 0x1F6D1;
+// The following categories should follow the required alphabetic order
+static const std::string RECENTLY_USED_CATEGORY = "1 recently used";
+static const std::string FREQUENTLY_USED_CATEGORY = "2 frequently used";
+
// Floater state related variables
-static U32 sSelectedGroupIndex = 0;
-static std::string sFilterPattern;
static std::list sRecentlyUsed;
static std::list> sFrequentlyUsed;
// State file related values
static std::string sStateFileName;
-static const std::string sKeySelectedGroupIndex("SelectedGroupIndex");
-static const std::string sKeyFilterPattern("FilterPattern");
static const std::string sKeyRecentlyUsed("RecentlyUsed");
static const std::string sKeyFrequentlyUsed("FrequentlyUsed");
}
@@ -104,11 +113,7 @@ public:
LLFontGL::VCENTER, // valign
LLFontGL::NORMAL, // style
LLFontGL::DROP_SHADOW_SOFT, // shadow
- mText.size(), // max_chars
- S32_MAX, // max_pixels
- nullptr, // right_x
- false, // use_ellipses
- true); // use_color
+ mText.size()); // max_chars
}
virtual void updatePanel(BOOL allow_modify) override {}
@@ -122,11 +127,10 @@ class LLEmojiGridIcon : public LLScrollingPanel
public:
LLEmojiGridIcon(
const LLPanel::Params& panel_params
- , const LLEmojiDescriptor* descr
- , std::string category)
+ , const LLEmojiSearchResult& emoji)
: LLScrollingPanel(panel_params)
- , mDescr(descr)
- , mText(LLWString(1, descr->Character))
+ , mData(emoji)
+ , mText(LLWString(1, emoji.Character))
{
}
@@ -146,21 +150,16 @@ public:
LLFontGL::VCENTER, // valign
LLFontGL::NORMAL, // style
LLFontGL::DROP_SHADOW_SOFT, // shadow
- 1, // max_chars
- S32_MAX, // max_pixels
- nullptr, // right_x
- false, // use_ellipses
- true); // use_color
+ 1); // max_chars
}
virtual void updatePanel(BOOL allow_modify) override {}
- const LLEmojiDescriptor* getDescr() const { return mDescr; }
- llwchar getEmoji() const { return mDescr->Character; }
+ const LLEmojiSearchResult& getData() const { return mData; }
LLWString getText() const { return mText; }
private:
- const LLEmojiDescriptor* mDescr;
+ const LLEmojiSearchResult mData;
const LLWString mText;
};
@@ -172,23 +171,31 @@ public:
{
}
- void setEmoji(const LLEmojiDescriptor* descr)
+ void setIcon(const LLEmojiGridIcon* icon)
{
- mDescr = descr;
+ if (icon)
+ {
+ setData(icon->getData().Character, icon->getData().String, icon->getData().Begin, icon->getData().End);
+ }
+ else
+ {
+ setData(0, LLStringUtil::null, 0, 0);
+ }
+ }
- if (!mDescr)
- return;
-
- mEmojiText = LLWString(1, descr->Character);
+ void setData(llwchar emoji, std::string title, size_t begin, size_t end)
+ {
+ mWStr = LLWString(1, emoji);
+ mEmoji = emoji;
+ mTitle = title;
+ mBegin = begin;
+ mEnd = end;
}
virtual void draw() override
{
LLPanel::draw();
- if (!mDescr)
- return;
-
S32 clientHeight = getRect().getHeight();
S32 clientWidth = getRect().getWidth();
S32 iconWidth = clientHeight;
@@ -200,24 +207,14 @@ public:
static LLColor4 defaultColor(0.75f, 0.75f, 0.75f, 1.0f);
LLColor4 textColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", defaultColor);
S32 max_pixels = clientWidth - iconWidth;
- size_t count = mDescr->ShortCodes.size();
- if (count == 1)
- {
- drawName(mDescr->ShortCodes.front(), iconWidth, centerY, max_pixels, textColor);
- }
- else if (count > 1)
- {
- F32 quarterY = 0.5f * centerY;
- drawName(mDescr->ShortCodes.front(), iconWidth, centerY + quarterY, max_pixels, textColor);
- drawName(*++mDescr->ShortCodes.begin(), iconWidth, quarterY, max_pixels, textColor);
- }
+ drawName(iconWidth, centerY, max_pixels, textColor);
}
protected:
void drawIcon(F32 x, F32 y, S32 max_pixels)
{
LLFontGL::getFontEmojiHuge()->render(
- mEmojiText, // wstr
+ mWStr, // wstr
0, // begin_offset
x, // x
y, // y
@@ -227,99 +224,121 @@ protected:
LLFontGL::NORMAL, // style
LLFontGL::DROP_SHADOW_SOFT, // shadow
1, // max_chars
- max_pixels, // max_pixels
- nullptr, // right_x
- false, // use_ellipses
- true); // use_color
+ max_pixels); // max_pixels
}
- void drawName(std::string name, F32 x, F32 y, S32 max_pixels, LLColor4& color)
+ void drawName(F32 x, F32 y, S32 max_pixels, LLColor4& color)
{
- LLFontGL::getFontEmoji()->renderUTF8(
- name, // wstr
- 0, // begin_offset
- x, // x
- y, // y
- color, // color
- LLFontGL::LEFT, // halign
- LLFontGL::VCENTER, // valign
- LLFontGL::NORMAL, // style
- LLFontGL::DROP_SHADOW_SOFT, // shadow
- -1, // max_chars
- max_pixels, // max_pixels
- nullptr, // right_x
- true, // use_ellipses
- false); // use_color
+ F32 x0 = x;
+ F32 x1 = max_pixels;
+ LLFontGL* font = LLFontGL::getFontEmoji();
+ if (mBegin)
+ {
+ std::string text = mTitle.substr(0, mBegin);
+ font->renderUTF8(
+ text, // text
+ 0, // begin_offset
+ x0, // x
+ y, // y
+ color, // color
+ LLFontGL::LEFT, // halign
+ LLFontGL::VCENTER, // valign
+ LLFontGL::NORMAL, // style
+ LLFontGL::DROP_SHADOW_SOFT, // shadow
+ text.size(), // max_chars
+ x1); // max_pixels
+ F32 dx = font->getWidthF32(text);
+ x0 += dx;
+ x1 -= dx;
+ }
+ if (x1 > 0 && mEnd > mBegin)
+ {
+ std::string text = mTitle.substr(mBegin, mEnd - mBegin);
+ font->renderUTF8(
+ text, // text
+ 0, // begin_offset
+ x0, // x
+ y, // y
+ LLColor4::yellow6, // color
+ LLFontGL::LEFT, // halign
+ LLFontGL::VCENTER, // valign
+ LLFontGL::NORMAL, // style
+ LLFontGL::DROP_SHADOW_SOFT, // shadow
+ text.size(), // max_chars
+ x1); // max_pixels
+ F32 dx = font->getWidthF32(text);
+ x0 += dx;
+ x1 -= dx;
+ }
+ if (x1 > 0 && mEnd < mTitle.size())
+ {
+ std::string text = mEnd ? mTitle.substr(mEnd) : mTitle;
+ font->renderUTF8(
+ text, // text
+ 0, // begin_offset
+ x0, // x
+ y, // y
+ color, // color
+ LLFontGL::LEFT, // halign
+ LLFontGL::VCENTER, // valign
+ LLFontGL::NORMAL, // style
+ LLFontGL::DROP_SHADOW_SOFT, // shadow
+ text.size(), // max_chars
+ x1); // max_pixels
+ }
}
private:
- const LLEmojiDescriptor* mDescr { nullptr };
- LLWString mEmojiText;
+ llwchar mEmoji;
+ LLWString mWStr;
+ std::string mTitle;
+ size_t mBegin;
+ size_t mEnd;
};
-LLFloaterEmojiPicker* LLFloaterEmojiPicker::getInstance()
-{
- LLFloaterEmojiPicker* floater = LLFloaterReg::getTypedInstance("emoji_picker");
- if (!floater)
- LL_ERRS() << "Cannot instantiate emoji picker" << LL_ENDL;
- return floater;
-}
-
-LLFloaterEmojiPicker* LLFloaterEmojiPicker::showInstance(pick_callback_t pick_callback, close_callback_t close_callback)
-{
- LLFloaterEmojiPicker* floater = getInstance();
- floater->show(pick_callback, close_callback);
- return floater;
-}
-
-void LLFloaterEmojiPicker::show(pick_callback_t pick_callback, close_callback_t close_callback)
-{
- mEmojiPickCallback = pick_callback;
- mFloaterCloseCallback = close_callback;
- openFloater(mKey);
- setFocus(TRUE);
-}
-
LLFloaterEmojiPicker::LLFloaterEmojiPicker(const LLSD& key)
: super(key)
{
+ // This floater should hover on top of our dependent (with the dependent having the focus)
+ setFocusStealsFrontmost(FALSE);
+ setBackgroundVisible(FALSE);
+ setAutoFocus(FALSE);
+
loadState();
}
BOOL LLFloaterEmojiPicker::postBuild()
{
- // Should be initialized first
+ mGroups = getChild("Groups");
+ mBadge = getChild("Badge");
+ mEmojiScroll = getChild("EmojiGridContainer");
+ mEmojiGrid = getChild("EmojiGrid");
+ mDummy = getChild("Dummy");
+
mPreview = new LLEmojiPreviewPanel();
mPreview->setVisible(FALSE);
addChild(mPreview);
- mDummy = getChild("Dummy");
+ return LLFloater::postBuild();
+}
- mGroups = getChild("Groups");
- mBadge = getChild("Badge");
+void LLFloaterEmojiPicker::onOpen(const LLSD& key)
+{
+ mHint = key["hint"].asString();
- mFilter = getChild("Filter");
- mFilter->setKeystrokeCallback([this](LLUICtrl*, const LLSD&) { onFilterChanged(); });
- mFilter->setTextChangedCallback([this](LLUICtrl*, const LLSD&) { onFilterChanged(); });
- mFilter->setText(sFilterPattern);
+ LLEmojiHelper::instance().setIsHideDisabled(mHint.empty());
+ mFilterPattern = mHint;
- mEmojiScroll = getChild("EmojiGridContainer");
- mEmojiScroll->setMouseEnterCallback([this](LLUICtrl*, const LLSD&) { onGridMouseEnter(); });
- mEmojiScroll->setMouseLeaveCallback([this](LLUICtrl*, const LLSD&) { onGridMouseLeave(); });
+ initialize();
- mEmojiGrid = getChild("EmojiGrid");
-
- fillGroups();
- fillEmojis();
-
- return TRUE;
+ gFloaterView->adjustToFitScreen(this, FALSE);
}
void LLFloaterEmojiPicker::dirtyRect()
{
super::dirtyRect();
- if (!mFilter)
+ if (!mPreview)
return;
const S32 HPADDING = 4;
@@ -330,20 +349,81 @@ void LLFloaterEmojiPicker::dirtyRect()
mPreview->setRect(rect);
}
- if (mEmojiScroll && mEmojiScroll->getRect().getWidth() != mRecentGridWidth)
+ if (mEmojiScroll && mEmojiGrid)
{
- moveGroups();
- fillEmojis(true);
+ S32 outer_width = mEmojiScroll->getRect().getWidth();
+ S32 inner_width = mEmojiGrid->getRect().getWidth();
+ if (outer_width != inner_width)
+ {
+ resizeGroupButtons();
+ fillEmojis(true);
+ }
}
}
-LLFloaterEmojiPicker::~LLFloaterEmojiPicker()
+void LLFloaterEmojiPicker::initialize()
{
- gFocusMgr.releaseFocusIfNeeded( this );
+ S32 groupIndex = mSelectedGroupIndex && mSelectedGroupIndex <= mFilteredEmojiGroups.size() ?
+ mFilteredEmojiGroups[mSelectedGroupIndex - 1] : ALL_EMOJIS_GROUP_INDEX;
+
+ fillGroups();
+
+ if (mFilteredEmojis.empty())
+ {
+ if (!mHint.empty())
+ {
+ hideFloater();
+ return;
+ }
+
+ mGroups->setVisible(FALSE);
+ mFocusedIconRow = -1;
+ mFocusedIconCol = -1;
+ mFocusedIcon = nullptr;
+ mHoveredIcon = nullptr;
+ mEmojiScroll->goToTop();
+ mEmojiGrid->clearPanels();
+
+ if (mFilterPattern.empty())
+ {
+ showPreview(false);
+ }
+ else
+ {
+ const std::string prompt("No emoji found for ");
+ std::string title(prompt + '"' + mFilterPattern.substr(1) + '"');
+ mPreview->setData(EMPTY_LIST_IMAGE_INDEX, title, prompt.size() + 1, title.size() - 1);
+ showPreview(true);
+ }
+ return;
+ }
+
+ mGroups->setVisible(TRUE);
+ mPreview->setIcon(nullptr);
+ showPreview(true);
+
+ mSelectedGroupIndex = groupIndex == ALL_EMOJIS_GROUP_INDEX ? 0 :
+ (1 + std::distance(mFilteredEmojiGroups.begin(),
+ std::find(mFilteredEmojiGroups.begin(), mFilteredEmojiGroups.end(), groupIndex))) %
+ (1 + mFilteredEmojiGroups.size());
+
+ mGroupButtons[mSelectedGroupIndex]->setToggleState(TRUE);
+ mGroupButtons[mSelectedGroupIndex]->setUseFontColor(TRUE);
+
+ fillEmojis();
}
void LLFloaterEmojiPicker::fillGroups()
{
+ // Do not use deleteAllChildren() because mBadge shouldn't be removed
+ for (LLButton* button : mGroupButtons)
+ {
+ mGroups->removeChild(button);
+ }
+ mFilteredEmojiGroups.clear();
+ mFilteredEmojis.clear();
+ mGroupButtons.clear();
+
LLButton::Params params;
params.font = LLFontGL::getFontEmoji();
@@ -351,281 +431,431 @@ void LLFloaterEmojiPicker::fillGroups()
rect.mTop = mGroups->getRect().getHeight();
rect.mBottom = mBadge->getRect().getHeight();
- int group_nr{ 0 }; // Fix mandatory name missing (XUI parser warning)
- const std::vector& groups = LLEmojiDictionary::instance().getGroups();
- for (const LLEmojiGroup& group : groups)
+ // Create button for "All categories"
+ params.name = "emojigroup_all_cagetories"; // Fix mandatory name missing (XUI parser warning)
+ createGroupButton(params, rect, ALL_EMOJIS_IMAGE_INDEX);
+
+ // Create group and button for "Recently used" and/or "Frequently used"
+ if (!sRecentlyUsed.empty() || !sFrequentlyUsed.empty())
{
- params.name = "emojigroup_" + std::to_string(group_nr++); // Fix mandatory name missing (XUI parser warning)
- LLButton* button = LLUICtrlFactory::create(params);
- button->setClickedCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonClick(ctrl); });
- button->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseEnter(ctrl); });
- button->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseLeave(ctrl); });
+ std::map> cats;
+ fillCategoryRecentlyUsed(cats);
+ fillCategoryFrequentlyUsed(cats);
- button->setRect(rect);
- button->setLabel(LLUIString(LLWString(1, group.Character)));
-
- if (mGroupButtons.size() == sSelectedGroupIndex)
+ if (!cats.empty())
{
- button->setToggleState(TRUE);
- button->setUseFontColor(TRUE);
+ mFilteredEmojiGroups.push_back(USED_EMOJIS_GROUP_INDEX);
+ mFilteredEmojis.emplace_back(cats);
+ params.name = "emojigroup_recently_frequently"; // Fix mandatory name missing (XUI parser warning)
+ createGroupButton(params, rect, USED_EMOJIS_IMAGE_INDEX);
}
-
- mGroupButtons.push_back(button);
- mGroups->addChild(button);
}
- moveGroups();
+ const std::vector& groups = LLEmojiDictionary::instance().getGroups();
+
+ // List all categories in the dictionary
+ for (U32 i = 0; i < groups.size(); ++i)
+ {
+ std::map> cats;
+
+ fillGroupEmojis(cats, i);
+
+ if (!cats.empty())
+ {
+ mFilteredEmojiGroups.push_back(i);
+ mFilteredEmojis.emplace_back(cats);
+ params.name = "emojigroup_" + std::to_string(i); // Fix mandatory name missing (XUI parser warning)
+ createGroupButton(params, rect, groups[i].Character);
+ }
+ }
+
+ resizeGroupButtons();
}
-void LLFloaterEmojiPicker::moveGroups()
+void LLFloaterEmojiPicker::fillCategoryRecentlyUsed(std::map>& cats)
+{
+ if (sRecentlyUsed.empty())
+ return;
+
+ std::vector emojis;
+
+ // In case of empty mFilterPattern we'd use sRecentlyUsed directly
+ if (!mFilterPattern.empty())
+ {
+ // List all emojis in "Recently used"
+ const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
+ std::size_t begin, end;
+ for (llwchar emoji : sRecentlyUsed)
+ {
+ auto e2d = emoji2descr.find(emoji);
+ if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
+ {
+ const std::string shortcode(e2d->second->ShortCodes.front());
+ if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern))
+ {
+ emojis.emplace_back(emoji, shortcode, begin, end);
+ }
+ }
+ }
+ if (emojis.empty())
+ return;
+ }
+
+ cats.emplace(std::make_pair(RECENTLY_USED_CATEGORY, emojis));
+}
+
+void LLFloaterEmojiPicker::fillCategoryFrequentlyUsed(std::map>& cats)
+{
+ if (sFrequentlyUsed.empty())
+ return;
+
+ std::vector emojis;
+
+ // In case of empty mFilterPattern we'd use sFrequentlyUsed directly
+ if (!mFilterPattern.empty())
+ {
+ // List all emojis in "Frequently used"
+ const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
+ std::size_t begin, end;
+ for (const auto& emoji : sFrequentlyUsed)
+ {
+ auto e2d = emoji2descr.find(emoji.first);
+ if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
+ {
+ const std::string shortcode(e2d->second->ShortCodes.front());
+ if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern))
+ {
+ emojis.emplace_back(emoji.first, shortcode, begin, end);
+ }
+ }
+ }
+ if (emojis.empty())
+ return;
+ }
+
+ cats.emplace(std::make_pair(FREQUENTLY_USED_CATEGORY, emojis));
+}
+
+void LLFloaterEmojiPicker::fillGroupEmojis(std::map>& cats, U32 index)
{
const std::vector& groups = LLEmojiDictionary::instance().getGroups();
- if (groups.empty())
+ const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs();
+
+ for (const std::string& category : groups[index].Categories)
+ {
+ const LLEmojiDictionary::cat2descrs_map_t::const_iterator& c2d = category2Descr.find(category);
+ if (c2d == category2Descr.end())
+ continue;
+
+ std::vector emojis;
+
+ // In case of empty mFilterPattern we'd use category2Descr directly
+ if (!mFilterPattern.empty())
+ {
+ // List all emojis in category
+ std::size_t begin, end;
+ for (const LLEmojiDescriptor* descr : c2d->second)
+ {
+ if (!descr->ShortCodes.empty())
+ {
+ const std::string shortcode(descr->ShortCodes.front());
+ if (LLEmojiDictionary::searchInShortCode(begin, end, shortcode, mFilterPattern))
+ {
+ emojis.emplace_back(descr->Character, shortcode, begin, end);
+ }
+ }
+ }
+ if (emojis.empty())
+ continue;
+ }
+
+ cats.emplace(std::make_pair(category, emojis));
+ }
+}
+
+void LLFloaterEmojiPicker::createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji)
+{
+ LLButton* button = LLUICtrlFactory::create(params);
+ button->setClickedCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonClick(ctrl); });
+ button->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseEnter(ctrl); });
+ button->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onGroupButtonMouseLeave(ctrl); });
+
+ button->setRect(rect);
+ button->setTabStop(FALSE);
+ button->setLabel(LLUIString(LLWString(1, emoji)));
+ button->setUseFontColor(FALSE);
+
+ mGroupButtons.push_back(button);
+ mGroups->addChild(button);
+}
+
+void LLFloaterEmojiPicker::resizeGroupButtons()
+{
+ U32 groupCount = (U32)mGroupButtons.size();
+ if (!groupCount)
return;
- int badgeWidth = mGroups->getRect().getWidth() / groups.size();
- if (badgeWidth == mRecentBadgeWidth)
- return;
+ S32 totalWidth = mGroups->getRect().getWidth();
+ S32 badgeWidth = totalWidth / groupCount;
+ S32 leftOffset = (totalWidth - badgeWidth * groupCount) / 2;
- mRecentBadgeWidth = badgeWidth;
-
- for (int i = 0; i < mGroupButtons.size(); ++i)
+ for (U32 i = 0; i < groupCount; ++i)
{
LLRect rect = mGroupButtons[i]->getRect();
- rect.mLeft = badgeWidth * i;
+ rect.mLeft = leftOffset + badgeWidth * i;
rect.mRight = rect.mLeft + badgeWidth;
mGroupButtons[i]->setRect(rect);
}
LLRect rect = mBadge->getRect();
- rect.mLeft = badgeWidth * sSelectedGroupIndex;
+ rect.mLeft = leftOffset + badgeWidth * mSelectedGroupIndex;
rect.mRight = rect.mLeft + badgeWidth;
mBadge->setRect(rect);
}
-void LLFloaterEmojiPicker::showPreview(bool show)
+void LLFloaterEmojiPicker::selectEmojiGroup(U32 index)
{
- mPreview->setEmoji(nullptr);
- mDummy->setVisible(show ? FALSE : TRUE);
- mPreview->setVisible(show ? TRUE : FALSE);
+ if (index == mSelectedGroupIndex || index >= mGroupButtons.size())
+ return;
+
+ if (mSelectedGroupIndex < mGroupButtons.size())
+ {
+ mGroupButtons[mSelectedGroupIndex]->setUseFontColor(FALSE);
+ mGroupButtons[mSelectedGroupIndex]->setToggleState(FALSE);
+ }
+
+ mSelectedGroupIndex = index;
+ mGroupButtons[mSelectedGroupIndex]->setToggleState(TRUE);
+ mGroupButtons[mSelectedGroupIndex]->setUseFontColor(TRUE);
+
+ LLButton* button = mGroupButtons[mSelectedGroupIndex];
+ LLRect rect = mBadge->getRect();
+ rect.mLeft = button->getRect().mLeft;
+ rect.mRight = button->getRect().mRight;
+ mBadge->setRect(rect);
+
+ fillEmojis();
}
void LLFloaterEmojiPicker::fillEmojis(bool fromResize)
{
- mRecentGridWidth = mEmojiScroll->getRect().getWidth();
-
- S32 scrollbarSize = mEmojiScroll->getSize();
- if (scrollbarSize < 0)
+ S32 scrollbar_size = mEmojiScroll->getSize();
+ if (scrollbar_size < 0)
{
static LLUICachedControl scrollbar_size_control("UIScrollbarSize", 0);
- scrollbarSize = scrollbar_size_control;
+ scrollbar_size = scrollbar_size_control;
}
- const S32 clientWidth = mRecentGridWidth - scrollbarSize - mEmojiScroll->getBorderWidth() * 2;
- const S32 gridPadding = mEmojiGrid->getPadding();
- const S32 iconSpacing = mEmojiGrid->getSpacing();
- const S32 rowWidth = clientWidth - gridPadding * 2;
- const S32 iconSize = 28; // icon width and height
- const S32 maxIcons = llmax(1, (rowWidth + iconSpacing) / (iconSize + iconSpacing));
+ const S32 scroll_width = mEmojiScroll->getRect().getWidth();
+ const S32 client_width = scroll_width - scrollbar_size - mEmojiScroll->getBorderWidth() * 2;
+ const S32 grid_padding = mEmojiGrid->getPadding();
+ const S32 icon_spacing = mEmojiGrid->getSpacing();
+ const S32 row_width = client_width - grid_padding * 2;
+ const S32 icon_size = 28; // icon width and height
+ const S32 max_icons = llmax(1, (row_width + icon_spacing) / (icon_size + icon_spacing));
// Optimization: don't rearrange for different widths with the same maxIcons
- if (fromResize && (maxIcons == mRecentMaxIcons))
+ if (fromResize && (max_icons == mRecentMaxIcons))
return;
- mRecentMaxIcons = maxIcons;
+ mRecentMaxIcons = max_icons;
mFocusedIconRow = 0;
mFocusedIconCol = 0;
mFocusedIcon = nullptr;
mHoveredIcon = nullptr;
+ mEmojiScroll->goToTop();
mEmojiGrid->clearPanels();
- mPreview->setEmoji(nullptr);
+ mPreview->setIcon(nullptr);
- if (mEmojiGrid->getRect().getWidth() != clientWidth)
+ if (mEmojiGrid->getRect().getWidth() != client_width)
{
LLRect rect = mEmojiGrid->getRect();
- rect.mRight = rect.mLeft + clientWidth;
+ rect.mRight = rect.mLeft + client_width;
mEmojiGrid->setRect(rect);
}
LLPanel::Params row_panel_params;
- row_panel_params.rect = LLRect(0, iconSize, rowWidth, 0);
+ row_panel_params.rect = LLRect(0, icon_size, row_width, 0);
LLScrollingPanelList::Params row_list_params;
row_list_params.rect = row_panel_params.rect;
row_list_params.is_horizontal = TRUE;
row_list_params.padding = 0;
- row_list_params.spacing = iconSpacing;
+ row_list_params.spacing = icon_spacing;
LLPanel::Params icon_params;
- LLRect icon_rect(0, iconSize, iconSize, 0);
+ LLRect icon_rect(0, icon_size, icon_size, 0);
- static LLColor4 defaultColor(0.75f, 0.75f, 0.75f, 1.0f);
- LLColor4 bgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", defaultColor);
+ static LLColor4 default_color(0.75f, 0.75f, 0.75f, 1.0f);
+ LLColor4 bg_color = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", default_color);
- auto matchesPattern = [](const LLEmojiDescriptor* descr) -> bool
+ if (!mSelectedGroupIndex)
{
- for (const std::string& shortCode : descr->ShortCodes)
- if (shortCode.find(sFilterPattern) != std::string::npos)
- return true;
- return false;
- };
-
- auto listCategory = [&](std::string category, const std::vector& emojis, int maxRows = 0)
- {
- int rowCount = 0;
- int iconIndex = 0;
- bool showDivider = true;
- bool mixedFolder = maxRows;
- LLEmojiGridRow* row = nullptr;
- if (!mixedFolder && !isupper(category.front()))
- {
- LLStringUtil::capitalize(category);
- }
-
- for (const LLEmojiDescriptor* descr : emojis)
- {
- if (sFilterPattern.empty() || matchesPattern(descr))
- {
- // Place a category title if needed
- if (showDivider)
- {
- LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, category);
- mEmojiGrid->addPanel(div, true);
- showDivider = false;
- }
-
- // Place a new row each (maxIcons) icons
- if (!(iconIndex % maxIcons))
- {
- if (maxRows && ++rowCount > maxRows)
- break;
- row = new LLEmojiGridRow(row_panel_params, row_list_params);
- mEmojiGrid->addPanel(row, true);
- }
-
- // Place a new icon to the current row
- LLEmojiGridIcon* icon = new LLEmojiGridIcon(icon_params, descr, mixedFolder ? LLStringUtil::capitalize(descr->Category) : category);
- icon->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseEnter(ctrl); });
- icon->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseLeave(ctrl); });
- icon->setMouseDownCallback([this](LLUICtrl* ctrl, S32, S32, MASK) { onEmojiMouseDown(ctrl); });
- icon->setMouseUpCallback([this](LLUICtrl* ctrl, S32, S32, MASK) { onEmojiMouseUp(ctrl); });
- icon->setBackgroundColor(bgColor);
- icon->setBackgroundOpaque(1);
- icon->setRect(icon_rect);
- row->mList->addPanel(icon, true);
-
- iconIndex++;
- }
- }
- };
-
- const std::vector& groups = LLEmojiDictionary::instance().getGroups();
- const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
- const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs();
- if (!sSelectedGroupIndex)
- {
- std::vector recentlyUsed;
- for (llwchar emoji : sRecentlyUsed)
- {
- auto it = emoji2descr.find(emoji);
- if (it != emoji2descr.end())
- {
- recentlyUsed.push_back(it->second);
- }
- }
- listCategory(getString("title_for_recently_used"), recentlyUsed, 1);
-
- std::vector frequentlyUsed;
- for (auto& emoji : sFrequentlyUsed)
- {
- auto it = emoji2descr.find(emoji.first);
- if (it != emoji2descr.end())
- {
- frequentlyUsed.push_back(it->second);
- }
- }
- listCategory(getString("title_for_frequently_used"), frequentlyUsed, 1);
-
// List all groups
- for (const LLEmojiGroup& group : groups)
+ for (const auto& group : mFilteredEmojis)
{
- // List all categories in group
- for (const std::string& category : group.Categories)
+ // List all categories in the group
+ for (const auto& category : group)
{
- // List all emojis in category
- const LLEmojiDictionary::cat2descrs_map_t::const_iterator& item = category2Descr.find(category);
- if (item != category2Descr.end())
- {
- listCategory(category, item->second);
- }
+ // List all emojis in the category
+ fillEmojisCategory(category.second, category.first, row_panel_params,
+ row_list_params, icon_params, icon_rect, max_icons, bg_color);
}
}
}
else
{
// List all categories in the selected group
- for (const std::string& category : groups[sSelectedGroupIndex].Categories)
+ const auto& group = mFilteredEmojis[mSelectedGroupIndex - 1];
+ for (const auto& category : group)
{
- // List all emojis in category
- const LLEmojiDictionary::cat2descrs_map_t::const_iterator& item = category2Descr.find(category);
- if (item != category2Descr.end())
+ // List all emojis in the category
+ fillEmojisCategory(category.second, category.first, row_panel_params,
+ row_list_params, icon_params, icon_rect, max_icons, bg_color);
+ }
+ }
+
+ if (mEmojiGrid->getPanelList().empty())
+ {
+ showPreview(false);
+ mFocusedIconRow = -1;
+ mFocusedIconCol = -1;
+ if (!mHint.empty())
+ {
+ hideFloater();
+ }
+ }
+ else
+ {
+ showPreview(true);
+ mFocusedIconRow = 0;
+ mFocusedIconCol = 0;
+ moveFocusedIconNext();
+ }
+}
+
+void LLFloaterEmojiPicker::fillEmojisCategory(const std::vector& emojis,
+ const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params,
+ const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg)
+{
+ // Place the category title
+ std::string title =
+ category == RECENTLY_USED_CATEGORY ? getString("title_for_recently_used") :
+ category == FREQUENTLY_USED_CATEGORY ? getString("title_for_frequently_used") :
+ isupper(category.front()) ? category : LLStringUtil::capitalize(category);
+ LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, title);
+ mEmojiGrid->addPanel(div, true);
+
+ int icon_index = 0;
+ LLEmojiGridRow* row = nullptr;
+
+ if (mFilterPattern.empty())
+ {
+ const LLEmojiDictionary::emoji2descr_map_t& emoji2descr = LLEmojiDictionary::instance().getEmoji2Descr();
+ LLEmojiSearchResult emoji { 0, "", 0, 0 };
+ if (category == RECENTLY_USED_CATEGORY)
+ {
+ for (llwchar code : sRecentlyUsed)
{
- listCategory(category, item->second);
+ const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code);
+ if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
+ {
+ emoji.Character = code;
+ emoji.String = e2d->second->ShortCodes.front();
+ createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
+ icon_rect, max_icons, bg, row, icon_index);
+ }
+ }
+ }
+ else if (category == FREQUENTLY_USED_CATEGORY)
+ {
+ for (const auto& code : sFrequentlyUsed)
+ {
+ const LLEmojiDictionary::emoji2descr_map_t::const_iterator& e2d = emoji2descr.find(code.first);
+ if (e2d != emoji2descr.end() && !e2d->second->ShortCodes.empty())
+ {
+ emoji.Character = code.first;
+ emoji.String = e2d->second->ShortCodes.front();
+ createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
+ icon_rect, max_icons, bg, row, icon_index);
+ }
+ }
+ }
+ else
+ {
+ const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs();
+ const LLEmojiDictionary::cat2descrs_map_t::const_iterator& c2d = category2Descr.find(category);
+ if (c2d != category2Descr.end())
+ {
+ for (const LLEmojiDescriptor* descr : c2d->second)
+ {
+ emoji.Character = descr->Character;
+ emoji.String = descr->ShortCodes.front();
+ createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
+ icon_rect, max_icons, bg, row, icon_index);
+ }
}
}
}
+ else
+ {
+ for (const LLEmojiSearchResult& emoji : emojis)
+ {
+ createEmojiIcon(emoji, category, row_panel_params, row_list_params, icon_params,
+ icon_rect, max_icons, bg, row, icon_index);
+ }
+ }
+}
+
+void LLFloaterEmojiPicker::createEmojiIcon(const LLEmojiSearchResult& emoji,
+ const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params,
+ const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg,
+ LLEmojiGridRow*& row, int& icon_index)
+{
+ // Place a new row each (max_icons) icons
+ if (!(icon_index % max_icons))
+ {
+ row = new LLEmojiGridRow(row_panel_params, *(const LLScrollingPanelList::Params*)&row_list_params);
+ mEmojiGrid->addPanel(row, true);
+ }
+
+ // Place a new icon to the current row
+ LLEmojiGridIcon* icon = new LLEmojiGridIcon(icon_params, emoji);
+ icon->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseEnter(ctrl); });
+ icon->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseLeave(ctrl); });
+ icon->setMouseDownCallback([this](LLUICtrl* ctrl, S32, S32, MASK) { onEmojiMouseDown(ctrl); });
+ icon->setMouseUpCallback([this](LLUICtrl* ctrl, S32, S32, MASK) { onEmojiMouseUp(ctrl); });
+ icon->setBackgroundColor(bg);
+ icon->setBackgroundOpaque(1);
+ icon->setRect(icon_rect);
+ row->mList->addPanel(icon, true);
+
+ icon_index++;
+}
+
+void LLFloaterEmojiPicker::showPreview(bool show)
+{
+ //mPreview->setIcon(nullptr);
+ mDummy->setVisible(show ? FALSE : TRUE);
+ mPreview->setVisible(show ? TRUE : FALSE);
}
void LLFloaterEmojiPicker::onGroupButtonClick(LLUICtrl* ctrl)
{
if (LLButton* button = dynamic_cast(ctrl))
{
- mFilter->setFocus(TRUE);
-
- if (button == mGroupButtons[sSelectedGroupIndex] || button->getToggleState())
+ if (button == mGroupButtons[mSelectedGroupIndex] || button->getToggleState())
return;
auto it = std::find(mGroupButtons.begin(), mGroupButtons.end(), button);
if (it == mGroupButtons.end())
return;
- mGroupButtons[sSelectedGroupIndex]->setUseFontColor(FALSE);
- mGroupButtons[sSelectedGroupIndex]->setToggleState(FALSE);
- sSelectedGroupIndex = it - mGroupButtons.begin();
- mGroupButtons[sSelectedGroupIndex]->setToggleState(TRUE);
- mGroupButtons[sSelectedGroupIndex]->setUseFontColor(TRUE);
-
- LLRect rect = mBadge->getRect();
- rect.mLeft = button->getRect().mLeft;
- rect.mRight = button->getRect().mRight;
- mBadge->setRect(rect);
-
- fillEmojis();
+ selectEmojiGroup(it - mGroupButtons.begin());
}
}
-void LLFloaterEmojiPicker::onFilterChanged()
-{
- sFilterPattern = mFilter->getText();
- fillEmojis();
-}
-
-void LLFloaterEmojiPicker::onGridMouseEnter()
-{
- LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
- if (focus == mEmojiGrid)
- {
- exitArrowMode();
- }
- showPreview(true);
-}
-
-void LLFloaterEmojiPicker::onGridMouseLeave()
-{
- showPreview(false);
-}
-
void LLFloaterEmojiPicker::onGroupButtonMouseEnter(LLUICtrl* ctrl)
{
if (LLButton* button = dynamic_cast(ctrl))
@@ -644,33 +874,40 @@ void LLFloaterEmojiPicker::onGroupButtonMouseLeave(LLUICtrl* ctrl)
void LLFloaterEmojiPicker::onEmojiMouseEnter(LLUICtrl* ctrl)
{
- if (ctrl)
+ if (LLEmojiGridIcon* icon = dynamic_cast(ctrl))
{
- LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
- if (focus == mEmojiGrid)
+ if (mFocusedIcon && mFocusedIcon != icon && mFocusedIcon->isBackgroundVisible())
{
- exitArrowMode();
- showPreview(true);
+ unselectGridIcon(mFocusedIcon);
}
- if (mHoveredIcon && mHoveredIcon != ctrl)
+ if (mHoveredIcon && mHoveredIcon != icon)
{
unselectGridIcon(mHoveredIcon);
}
- selectGridIcon(ctrl);
+ selectGridIcon(icon);
- mHoveredIcon = ctrl;
+ mHoveredIcon = icon;
}
}
void LLFloaterEmojiPicker::onEmojiMouseLeave(LLUICtrl* ctrl)
{
- if (ctrl)
+ if (LLEmojiGridIcon* icon = dynamic_cast(ctrl))
{
- if (ctrl == mHoveredIcon)
+ if (icon == mHoveredIcon)
{
- unselectGridIcon(ctrl);
+ if (icon != mFocusedIcon)
+ {
+ unselectGridIcon(icon);
+ }
+ mHoveredIcon = nullptr;
+ }
+
+ if (!mHoveredIcon && mFocusedIcon && !mFocusedIcon->isBackgroundVisible())
+ {
+ selectGridIcon(mFocusedIcon);
}
}
}
@@ -690,141 +927,109 @@ void LLFloaterEmojiPicker::onEmojiMouseUp(LLUICtrl* ctrl)
make_ui_sound("UISndClickRelease");
}
- if (mEmojiPickCallback)
+ if (LLEmojiGridIcon* icon = dynamic_cast(ctrl))
{
- if (LLEmojiGridIcon* icon = dynamic_cast(ctrl))
+ LLSD value(wstring_to_utf8str(icon->getText()));
+ setValue(value);
+
+ onCommit();
+
+ if (!mHint.empty() || !(gKeyboard->currentMask(TRUE) & MASK_SHIFT))
{
- if (mEmojiPickCallback)
- {
- mEmojiPickCallback(icon->getEmoji());
- }
+ hideFloater();
}
}
}
-bool LLFloaterEmojiPicker::enterArrowMode()
-{
- S32 rowCount = mEmojiGrid->getPanelList().size();
- if (rowCount)
- {
- mFocusedIconRow = -1;
- mFocusedIconCol = 0;
- if (moveFocusedIconDown())
- {
- showPreview(true);
- mEmojiScroll->goToTop();
- mEmojiGrid->setFocus(TRUE);
- return true;
- }
- }
- return false;
-}
-
-void LLFloaterEmojiPicker::exitArrowMode()
-{
- if (mFocusedIcon)
- {
- unselectGridIcon(mFocusedIcon);
- mFocusedIcon = nullptr;
- }
-
- showPreview(false);
- mEmojiScroll->goToTop();
- mFocusedIconRow = mFocusedIconCol = 0;
- mFilter->setFocus(TRUE);
-}
-
void LLFloaterEmojiPicker::selectFocusedIcon()
{
- if (mHoveredIcon)
- {
- unselectGridIcon(mHoveredIcon);
- }
-
- if (mFocusedIcon)
+ if (mFocusedIcon && mFocusedIcon != mHoveredIcon)
{
unselectGridIcon(mFocusedIcon);
}
// Both mFocusedIconRow and mFocusedIconCol should be already verified
- LLEmojiGridRow* row = (LLEmojiGridRow*)mEmojiGrid->getPanelList()[mFocusedIconRow];
- mFocusedIcon = row->mList->getPanelList()[mFocusedIconCol];
- selectGridIcon(mFocusedIcon);
+ LLEmojiGridRow* row = dynamic_cast(mEmojiGrid->getPanelList()[mFocusedIconRow]);
+ mFocusedIcon = row ? dynamic_cast(row->mList->getPanelList()[mFocusedIconCol]) : nullptr;
+
+ if (mFocusedIcon && !mHoveredIcon)
+ {
+ selectGridIcon(mFocusedIcon);
+ }
}
-bool LLFloaterEmojiPicker::moveFocusedIconUp()
+bool LLFloaterEmojiPicker::moveFocusedIconPrev()
{
+ if (mHoveredIcon)
+ return false;
+
+ if (mFocusedIconCol > 0)
+ {
+ mFocusedIconCol--;
+ selectFocusedIcon();
+ return true;
+ }
+
for (S32 i = mFocusedIconRow - 1; i >= 0; --i)
{
LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i];
LLEmojiGridRow* row = dynamic_cast(panel);
- if (row && row->mList->getPanelList().size() > mFocusedIconCol)
+ if (row && row->mList->getPanelList().size())
{
mEmojiScroll->scrollToShowRect(row->getBoundingRect());
+ mFocusedIconCol = row->mList->getPanelList().size() - 1;
mFocusedIconRow = i;
selectFocusedIcon();
return true;
}
}
+
return false;
}
-bool LLFloaterEmojiPicker::moveFocusedIconDown()
+bool LLFloaterEmojiPicker::moveFocusedIconNext()
{
+ if (mHoveredIcon)
+ return false;
+
+ LLScrollingPanel* panel = mEmojiGrid->getPanelList()[mFocusedIconRow];
+ LLEmojiGridRow* row = dynamic_cast(panel);
+ S32 colCount = row ? row->mList->getPanelList().size() : 0;
+ if (mFocusedIconCol < colCount - 1)
+ {
+ mFocusedIconCol++;
+ selectFocusedIcon();
+ return true;
+ }
+
S32 rowCount = mEmojiGrid->getPanelList().size();
for (S32 i = mFocusedIconRow + 1; i < rowCount; ++i)
{
LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i];
LLEmojiGridRow* row = dynamic_cast(panel);
- if (row && row->mList->getPanelList().size() > mFocusedIconCol)
+ if (row && row->mList->getPanelList().size())
{
mEmojiScroll->scrollToShowRect(row->getBoundingRect());
+ mFocusedIconCol = 0;
mFocusedIconRow = i;
selectFocusedIcon();
return true;
}
}
+
return false;
}
-bool LLFloaterEmojiPicker::moveFocusedIconLeft()
+void LLFloaterEmojiPicker::selectGridIcon(LLEmojiGridIcon* icon)
{
- if (mFocusedIconCol <= 0)
- return false;
-
- mFocusedIconCol--;
- selectFocusedIcon();
- return true;
+ icon->setBackgroundVisible(TRUE);
+ mPreview->setIcon(icon);
}
-bool LLFloaterEmojiPicker::moveFocusedIconRight()
+void LLFloaterEmojiPicker::unselectGridIcon(LLEmojiGridIcon* icon)
{
- LLEmojiGridRow* row = (LLEmojiGridRow*)mEmojiGrid->getPanelList()[mFocusedIconRow];
- S32 colCount = row->mList->getPanelList().size();
- if (mFocusedIconCol >= colCount - 1)
- return false;
-
- mFocusedIconCol++;
- selectFocusedIcon();
- return true;
-}
-
-void LLFloaterEmojiPicker::selectGridIcon(LLUICtrl* ctrl)
-{
- if (LLEmojiGridIcon* icon = dynamic_cast(ctrl))
- {
- icon->setBackgroundVisible(TRUE);
- mPreview->setEmoji(icon->getDescr());
- }
-}
-
-void LLFloaterEmojiPicker::unselectGridIcon(LLUICtrl* ctrl)
-{
- if (LLEmojiGridIcon* icon = dynamic_cast(ctrl))
- {
- icon->setBackgroundVisible(FALSE);
- mPreview->setEmoji(nullptr);
- }
+ icon->setBackgroundVisible(FALSE);
+ mPreview->setIcon(nullptr);
}
// virtual
@@ -832,53 +1037,66 @@ BOOL LLFloaterEmojiPicker::handleKey(KEY key, MASK mask, BOOL called_from_parent
{
if (mask == MASK_NONE)
{
- LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
- if (focus == mEmojiGrid)
+ switch (key)
{
- if (key == KEY_RETURN)
- {
- if (mFocusedIcon)
- {
- onEmojiMouseDown(mFocusedIcon);
- onEmojiMouseUp(mFocusedIcon);
- closeFloater();
- return TRUE;
- }
- }
- else if (key == KEY_TAB)
- {
- exitArrowMode();
- return TRUE;
- }
- else if (key == KEY_UP)
- {
- if (!moveFocusedIconUp())
- exitArrowMode();
- return TRUE;
- }
- else if (key == KEY_DOWN)
- {
- if (moveFocusedIconDown())
- return TRUE;
- }
- else if (key == KEY_LEFT)
- {
- if (moveFocusedIconLeft())
- return TRUE;
- }
- else if (key == KEY_RIGHT)
- {
- if (moveFocusedIconRight())
- return TRUE;
- }
+ case KEY_LEFT:
+ selectEmojiGroup((mSelectedGroupIndex + mFilteredEmojis.size()) % mGroupButtons.size());
+ return TRUE;
+ case KEY_RIGHT:
+ selectEmojiGroup((mSelectedGroupIndex + 1) % mGroupButtons.size());
+ return TRUE;
+ case KEY_UP:
+ moveFocusedIconPrev();
+ return TRUE;
+ case KEY_DOWN:
+ moveFocusedIconNext();
+ return TRUE;
+ case KEY_ESCAPE:
+ hideFloater();
+ return TRUE;
}
- else // if (focus != mEmojiGrid)
+ }
+
+ if (key == KEY_RETURN)
+ {
+ U64 time = totalTime();
+ // comes twice for unknown reason
+ if (mFocusedIcon && (time - mRecentReturnPressedMs > 100000)) // Min interval 0.1 sec.
{
- if (key == KEY_DOWN)
+ onEmojiMouseDown(mFocusedIcon);
+ onEmojiMouseUp(mFocusedIcon);
+ }
+ mRecentReturnPressedMs = time;
+ return TRUE;
+ }
+
+ if (mHint.empty())
+ {
+ if (key >= 0x20 && key < 0x80)
+ {
+ if (!mEmojiGrid->getPanelList().empty())
{
- if (enterArrowMode())
- return TRUE;
+ if (mFilterPattern.empty())
+ {
+ mFilterPattern = ":";
+ }
+ mFilterPattern += (char)key;
+ initialize();
}
+ return TRUE;
+ }
+ else if (key == KEY_BACKSPACE)
+ {
+ if (!mFilterPattern.empty())
+ {
+ mFilterPattern.pop_back();
+ if (mFilterPattern == ":")
+ {
+ mFilterPattern.clear();
+ }
+ initialize();
+ }
+ return TRUE;
}
}
@@ -886,30 +1104,14 @@ BOOL LLFloaterEmojiPicker::handleKey(KEY key, MASK mask, BOOL called_from_parent
}
// virtual
-BOOL LLFloaterEmojiPicker::handleKeyHere(KEY key, MASK mask)
+void LLFloaterEmojiPicker::goneFromFront()
{
- if (mask == MASK_NONE)
- {
- switch (key)
- {
- case KEY_ESCAPE:
- closeFloater();
- return TRUE;
- }
- }
-
- return super::handleKeyHere(key, mask);
+ hideFloater();
}
-// virtual
-void LLFloaterEmojiPicker::closeFloater(bool app_quitting)
+void LLFloaterEmojiPicker::hideFloater() const
{
- saveState();
- super::closeFloater(app_quitting);
- if (mFloaterCloseCallback)
- {
- mFloaterCloseCallback();
- }
+ LLEmojiHelper::instance().hideHelper(nullptr, true);
}
// static
@@ -956,22 +1158,21 @@ void LLFloaterEmojiPicker::onEmojiUsed(llwchar emoji)
}
// Append new if not found
if (itf == sFrequentlyUsed.end())
- sFrequentlyUsed.push_back(std::make_pair(emoji, 1));
-
- // Live-update recently used emojis
- sRecentEmojisUpdatedCallback(sRecentlyUsed);
-}
-
-// static
-void LLFloaterEmojiPicker::onRecentlyUsedChanged()
-{
- if (sSelectedGroupIndex)
- return;
-
- if (LLFloaterEmojiPicker* picker = getInstance())
{
- picker->fillEmojis();
+ // Insert before others with count == 1
+ while (itf != sFrequentlyUsed.begin())
+ {
+ auto prior = itf;
+ prior--;
+ if (prior->second > 1)
+ break;
+ itf = prior;
+ }
+ sFrequentlyUsed.insert(itf, std::make_pair(emoji, 1));
}
+
+ // Live-update recently used emoji
+ sRecentEmojisUpdatedCallback(sRecentlyUsed);
}
// static
@@ -980,7 +1181,7 @@ void LLFloaterEmojiPicker::loadState()
if (!sStateFileName.empty())
return; // Already loaded
- sStateFileName = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "emoji_floater_state.xml");
+ sStateFileName = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "emoji_floater_state.xml");
llifstream file;
file.open(sStateFileName.c_str());
@@ -998,10 +1199,6 @@ void LLFloaterEmojiPicker::loadState()
return;
}
- sSelectedGroupIndex = state[sKeySelectedGroupIndex].asInteger();
-
- sFilterPattern = state[sKeyFilterPattern].asString();
-
// Load and parse sRecentlyUsed
std::string recentlyUsed = state[sKeyRecentlyUsed];
std::vector rtokens = LLStringUtil::getTokens(recentlyUsed, ",");
@@ -1049,7 +1246,7 @@ void LLFloaterEmojiPicker::loadState()
// Normalize by minimum
if (!sFrequentlyUsed.empty())
{
- U32 delta = sFrequentlyUsed.back().second;
+ U32 delta = sFrequentlyUsed.back().second - 1;
for (auto& it : sFrequentlyUsed)
{
it.second = std::max((U32)0, it.second - delta);
@@ -1068,16 +1265,6 @@ void LLFloaterEmojiPicker::saveState()
LLSD state = LLSD::emptyMap();
- if (sSelectedGroupIndex)
- {
- state[sKeySelectedGroupIndex] = (int)sSelectedGroupIndex;
- }
-
- if (!sFilterPattern.empty())
- {
- state[sKeyFilterPattern] = sFilterPattern;
- }
-
if (!sRecentlyUsed.empty())
{
U32 maxCount = 20;
diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h
index 9b70368a93..518ed1b591 100644
--- a/indra/newview/llfloateremojipicker.h
+++ b/indra/newview/llfloateremojipicker.h
@@ -29,7 +29,10 @@
#include "llfloater.h"
+class LLEmojiGridRow;
+class LLEmojiGridIcon;
struct LLEmojiDescriptor;
+struct LLEmojiSearchResult;
class LLFloaterEmojiPicker : public LLFloater
{
@@ -40,23 +43,17 @@ public:
typedef boost::function pick_callback_t;
typedef boost::function close_callback_t;
- // Call this to select an emoji.
- static LLFloaterEmojiPicker* getInstance();
- static LLFloaterEmojiPicker* showInstance(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr);
-
LLFloaterEmojiPicker(const LLSD& key);
- virtual ~LLFloaterEmojiPicker();
virtual BOOL postBuild() override;
virtual void dirtyRect() override;
+ virtual void goneFromFront() override;
- void show(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr);
-
- virtual void closeFloater(bool app_quitting = false) override;
+ void hideFloater() const;
static std::list& getRecentlyUsed();
static void onEmojiUsed(llwchar emoji);
- static void onRecentlyUsedChanged();
+
static void loadState();
static void saveState();
@@ -69,15 +66,25 @@ public:
//
private:
+ void initialize();
void fillGroups();
- void moveGroups();
- void showPreview(bool show);
+ void fillCategoryRecentlyUsed(std::map>& cats);
+ void fillCategoryFrequentlyUsed(std::map>& cats);
+ void fillGroupEmojis(std::map>& cats, U32 index);
+ void createGroupButton(LLButton::Params& params, const LLRect& rect, llwchar emoji);
+ void resizeGroupButtons();
+ void selectEmojiGroup(U32 index);
void fillEmojis(bool fromResize = false);
+ void fillEmojisCategory(const std::vector& emojis,
+ const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params,
+ const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg);
+ void createEmojiIcon(const LLEmojiSearchResult& emoji,
+ const std::string& category, const LLPanel::Params& row_panel_params, const LLUICtrl::Params& row_list_params,
+ const LLPanel::Params& icon_params, const LLRect& icon_rect, S32 max_icons, const LLColor4& bg,
+ LLEmojiGridRow*& row, int& icon_index);
+ void showPreview(bool show);
void onGroupButtonClick(LLUICtrl* ctrl);
- void onFilterChanged();
- void onGridMouseEnter();
- void onGridMouseLeave();
void onGroupButtonMouseEnter(LLUICtrl* ctrl);
void onGroupButtonMouseLeave(LLUICtrl* ctrl);
void onEmojiMouseEnter(LLUICtrl* ctrl);
@@ -85,40 +92,37 @@ private:
void onEmojiMouseDown(LLUICtrl* ctrl);
void onEmojiMouseUp(LLUICtrl* ctrl);
- bool enterArrowMode();
- void exitArrowMode();
void selectFocusedIcon();
- bool moveFocusedIconUp();
- bool moveFocusedIconDown();
- bool moveFocusedIconLeft();
- bool moveFocusedIconRight();
+ bool moveFocusedIconPrev();
+ bool moveFocusedIconNext();
- void selectGridIcon(LLUICtrl* ctrl);
- void unselectGridIcon(LLUICtrl* ctrl);
+ void selectGridIcon(LLEmojiGridIcon* icon);
+ void unselectGridIcon(LLEmojiGridIcon* icon);
+ void onOpen(const LLSD& key) override;
virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent) override;
- virtual BOOL handleKeyHere(KEY key, MASK mask) override;
class LLPanel* mGroups { nullptr };
class LLPanel* mBadge { nullptr };
- class LLFilterEditor* mFilter { nullptr };
class LLScrollContainer* mEmojiScroll { nullptr };
class LLScrollingPanelList* mEmojiGrid { nullptr };
class LLEmojiPreviewPanel* mPreview { nullptr };
class LLTextBox* mDummy { nullptr };
- pick_callback_t mEmojiPickCallback;
- close_callback_t mFloaterCloseCallback;
-
+ std::vector mFilteredEmojiGroups;
+ std::vector>> mFilteredEmojis;
std::vector mGroupButtons;
- S32 mRecentBadgeWidth { 0 };
- S32 mRecentGridWidth { 0 };
+ std::string mHint;
+ std::string mFilterPattern;
+ U32 mSelectedGroupIndex { 0 };
S32 mRecentMaxIcons { 0 };
S32 mFocusedIconRow { 0 };
S32 mFocusedIconCol { 0 };
- LLUICtrl* mFocusedIcon { nullptr };
- LLUICtrl* mHoveredIcon { nullptr };
+ LLEmojiGridIcon* mFocusedIcon { nullptr };
+ LLEmojiGridIcon* mHoveredIcon { nullptr };
+
+ U64 mRecentReturnPressedMs { 0 };
// Live-update recently used emojis
static recent_emojis_updated_callback_t sRecentEmojisUpdatedCallback;
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
index 2e9cdc0f35..04d35a032a 100644
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -260,8 +260,7 @@ BOOL LLFloaterIMSessionTab::postBuild()
mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this));
mEmojiRecentPanelToggleBtn = getChild("emoji_recent_panel_toggle_btn");
- mEmojiRecentPanelToggleBtn->setLabel(LLUIString(LLWString(1, 128512)));
- mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(this); });
+ mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(); });
mEmojiRecentPanel = getChild("emoji_recent_layout_panel");
mEmojiRecentPanel->setVisible(false);
@@ -274,8 +273,8 @@ BOOL LLFloaterIMSessionTab::postBuild()
mEmojiRecentIconsCtrl->setCommitCallback([this](LLUICtrl*, const LLSD& value) { onRecentEmojiPicked(value); });
mEmojiRecentIconsCtrl->setVisible(false);
- mEmojiPickerToggleBtn = getChild("emoji_picker_toggle_btn");
- mEmojiPickerToggleBtn->setClickedCallback([](LLUICtrl*, const LLSD&) { onEmojiPickerToggleBtnClicked(); });
+ mEmojiPickerShowBtn = getChild("emoji_picker_show_btn");
+ mEmojiPickerShowBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnClicked(); });
mGearBtn = getChild("gear_btn");
mAddBtn = getChild("add_btn");
@@ -454,56 +453,30 @@ void LLFloaterIMSessionTab::onInputEditorClicked()
gToolBarView->flashCommand(LLCommandId("chat"), false);
}
-void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked(LLFloaterIMSessionTab* self)
+void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked()
{
- bool show = !self->mEmojiRecentPanel->getVisible();
- bool restore_focus = !show || (gFocusMgr.getLastKeyboardFocus() == self->mInputEditor);
-
+ BOOL show = mEmojiRecentPanel->getVisible() ? FALSE : TRUE;
if (show)
{
- self->initEmojiRecentPanel(!restore_focus);
+ initEmojiRecentPanel();
}
- self->mEmojiRecentPanel->setVisible(show ? TRUE : FALSE);
-
- if (restore_focus)
- {
- self->mInputEditor->setFocus(true);
- }
+ mEmojiRecentPanel->setVisible(show);
+ mInputEditor->setFocus(TRUE);
}
-void LLFloaterIMSessionTab::onEmojiPickerToggleBtnClicked()
+void LLFloaterIMSessionTab::onEmojiPickerShowBtnClicked()
{
- if (LLFloaterEmojiPicker* picker = LLFloaterEmojiPicker::getInstance())
- {
- if (!picker->isShown())
- {
- picker->show(
- [](llwchar emoji) { onEmojiPicked(emoji); },
- []() { onEmojiPickerClosed(); });
- if (LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance())
- {
- im_box->addDependentFloater(picker, TRUE, TRUE);
- }
- }
- else
- {
- picker->closeFloater();
- }
- }
+ mInputEditor->showEmojiHelper();
}
-void LLFloaterIMSessionTab::initEmojiRecentPanel(bool moveFocus)
+void LLFloaterIMSessionTab::initEmojiRecentPanel()
{
std::list& recentlyUsed = LLFloaterEmojiPicker::getRecentlyUsed();
if (recentlyUsed.empty())
{
mEmojiRecentEmptyText->setVisible(TRUE);
mEmojiRecentIconsCtrl->setVisible(FALSE);
- if (moveFocus)
- {
- mEmojiPickerToggleBtn->setFocus(TRUE);
- }
}
else
{
@@ -515,10 +488,6 @@ void LLFloaterIMSessionTab::initEmojiRecentPanel(bool moveFocus)
mEmojiRecentIconsCtrl->setEmojis(emojis);
mEmojiRecentEmptyText->setVisible(FALSE);
mEmojiRecentIconsCtrl->setVisible(TRUE);
- if (moveFocus)
- {
- mEmojiRecentIconsCtrl->setFocus(TRUE);
- }
}
}
@@ -536,31 +505,6 @@ void LLFloaterIMSessionTab::onRecentEmojiPicked(const LLSD& value)
}
}
-// static
-void LLFloaterIMSessionTab::onEmojiPicked(llwchar emoji)
-{
- if (LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance())
- {
- if (LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(im_box->getSelectedSession()))
- {
- session_floater->mInputEditor->insertEmoji(emoji);
- session_floater->mInputEditor->setFocus(TRUE);
- }
- }
-}
-
-// static
-void LLFloaterIMSessionTab::onEmojiPickerClosed()
-{
- if (LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance())
- {
- if (LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(im_box->getSelectedSession()))
- {
- session_floater->mInputEditor->setFocus(TRUE);
- }
- }
-}
-
void LLFloaterIMSessionTab::closeFloater(bool app_quitting)
{
LLFloaterEmojiPicker::saveState();
@@ -626,11 +570,11 @@ void LLFloaterIMSessionTab::updateUsedEmojis(LLWString text)
if (!emojiSent)
return;
- LLFloaterEmojiPicker::onRecentlyUsedChanged();
+ LLFloaterEmojiPicker::saveState();
if (mEmojiRecentPanel->getVisible())
{
- initEmojiRecentPanel(false);
+ initEmojiRecentPanel();
}
}
diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h
index 1a54841c89..7464d2e7a0 100644
--- a/indra/newview/llfloaterimsessiontab.h
+++ b/indra/newview/llfloaterimsessiontab.h
@@ -191,7 +191,7 @@ protected:
LLButton* mExpandCollapseBtn;
LLButton* mTearOffBtn;
LLButton* mEmojiRecentPanelToggleBtn;
- LLButton* mEmojiPickerToggleBtn;
+ LLButton* mEmojiPickerShowBtn;
LLButton* mCloseBtn;
LLButton* mGearBtn;
LLButton* mAddBtn;
@@ -217,12 +217,10 @@ private:
void onInputEditorClicked();
- static void onEmojiRecentPanelToggleBtnClicked(LLFloaterIMSessionTab* self);
- static void onEmojiPickerToggleBtnClicked();
- void initEmojiRecentPanel(bool moveFocus);
+ void onEmojiRecentPanelToggleBtnClicked();
+ void onEmojiPickerShowBtnClicked();
+ void initEmojiRecentPanel();
void onRecentEmojiPicked(const LLSD& value);
- static void onEmojiPicked(llwchar emoji);
- static void onEmojiPickerClosed();
bool checkIfTornOff();
bool mIsHostAttached;
diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp
index f7ba8449e9..d1ad51a7e9 100644
--- a/indra/newview/llfloateruipreview.cpp
+++ b/indra/newview/llfloateruipreview.cpp
@@ -1632,7 +1632,7 @@ void LLOverlapPanel::draw()
LLUI::translate(5,getRect().getHeight()-20); // translate to top-5,left-5
LLView::sDrawPreviewHighlights = FALSE;
LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text, 0, 0, 0, text_color,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
}
else
{
@@ -1650,7 +1650,7 @@ void LLOverlapPanel::draw()
std::string current_selection = std::string(current_selection_text + LLView::sPreviewClickedElement->getName() + " (no elements overlap)");
S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(current_selection) + 10;
LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection, 0, 0, 0, text_color,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
// widen panel enough to fit this text
LLRect rect = getRect();
setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
@@ -1716,7 +1716,7 @@ void LLOverlapPanel::draw()
// draw currently-selected element at top of overlappers
LLUI::translate(0,-mSpacing);
LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text + LLView::sPreviewClickedElement->getName(), 0, 0, 0, text_color,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
LLUI::translate(0,-mSpacing-LLView::sPreviewClickedElement->getRect().getHeight()); // skip spacing distance + height
LLView::sPreviewClickedElement->draw();
@@ -1731,7 +1731,7 @@ void LLOverlapPanel::draw()
// draw name
LLUI::translate(0,-mSpacing);
LLFontGL::getFontSansSerifSmall()->renderUTF8(overlapper_text + viewp->getName(), 0, 0, 0, text_color,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
// draw element
LLUI::translate(0,-mSpacing-viewp->getRect().getHeight()); // skip spacing distance + height
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index f4d9275d48..9f696f2787 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -2231,7 +2231,7 @@ bool LLMaterialEditor::canModifyObjectsMaterial()
LLSelectedTEGetMatData func(true);
LLPermissions permissions;
LLViewerInventoryItem* item_out;
- return can_use_objects_material(func, std::vector({PERM_MODIFY}), ItemSource::OBJECT, permissions, item_out);
+ return can_use_objects_material(func, std::vector({PERM_MODIFY}), ItemSource::OBJECT, permissions, item_out);
}
bool LLMaterialEditor::canSaveObjectsMaterial()
@@ -2239,7 +2239,7 @@ bool LLMaterialEditor::canSaveObjectsMaterial()
LLSelectedTEGetMatData func(true);
LLPermissions permissions;
LLViewerInventoryItem* item_out;
- return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), ItemSource::AGENT, permissions, item_out);
+ return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), ItemSource::AGENT, permissions, item_out);
}
bool LLMaterialEditor::canClipboardObjectsMaterial()
@@ -2265,7 +2265,7 @@ bool LLMaterialEditor::canClipboardObjectsMaterial()
LLSelectedTEGetMatData func(true);
LLPermissions permissions;
LLViewerInventoryItem* item_out;
- return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY, PERM_TRANSFER}), ItemSource::OBJECT, permissions, item_out);
+ return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY, PERM_TRANSFER}), ItemSource::OBJECT, permissions, item_out);
}
void LLMaterialEditor::saveObjectsMaterialAs()
@@ -2273,7 +2273,7 @@ void LLMaterialEditor::saveObjectsMaterialAs()
LLSelectedTEGetMatData func(true);
LLPermissions permissions;
LLViewerInventoryItem* item = nullptr;
- bool allowed = can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), ItemSource::AGENT, permissions, item);
+ bool allowed = can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), ItemSource::AGENT, permissions, item);
if (!allowed)
{
LL_WARNS("MaterialEditor") << "Failed to save GLTF material from object" << LL_ENDL;
diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp
index 9bfe04fc31..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;
}
@@ -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;
}
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index 563cac4995..704ff74ae0 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -687,8 +687,7 @@ void LLGLTexMemBar::draw()
x_right = 550.0;
LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,
text_color, LLFontGL::LEFT, LLFontGL::TOP,
- LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX,
- &x_right, /*use_ellipses*/FALSE, /*use_color*/FALSE);
+ LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &x_right);
// Move BW figures further to the right to prevent overlapping
left = 575;
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 27d7204462..7ef40dbe7f 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1058,8 +1058,7 @@ public:
{
const Line& line = *iter;
LLFontGL::getFontMonospace()->renderUTF8(line.text, 0, (F32)line.x, (F32)line.y, mTextColor,
- LLFontGL::LEFT, LLFontGL::TOP,
- LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, /*use_ellipses*/FALSE, /*use_color*/FALSE);
+ LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
}
}
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index 131f1263af..1822ced2ca 100644
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -608,8 +608,7 @@ void LLWorldMapView::draw()
S32_MAX, //max_chars
mMapScale, //max_pixels
NULL,
- /*use_ellipses*/TRUE,
- /*use_color*/FALSE);
+ /*use_ellipses*/TRUE);
if (drawAdvancedRegionInfo)
{
diff --git a/indra/newview/skins/default/textures/icons/emoji_picker_icon.png b/indra/newview/skins/default/textures/icons/emoji_picker_icon.png
new file mode 100644
index 0000000000..ad4f3fa63c
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/emoji_picker_icon.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 07a9ede6d2..08d3dd3e57 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -222,6 +222,7 @@ with the same filename but different name
+
diff --git a/indra/newview/skins/default/xui/de/floater_fs_im_session.xml b/indra/newview/skins/default/xui/de/floater_fs_im_session.xml
index 48a8bcbe68..b52e8b97bd 100644
--- a/indra/newview/skins/default/xui/de/floater_fs_im_session.xml
+++ b/indra/newview/skins/default/xui/de/floater_fs_im_session.xml
@@ -66,7 +66,7 @@
-
+
diff --git a/indra/newview/skins/default/xui/de/floater_fs_nearby_chat.xml b/indra/newview/skins/default/xui/de/floater_fs_nearby_chat.xml
index 48370d3c71..1830ae7423 100644
--- a/indra/newview/skins/default/xui/de/floater_fs_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/de/floater_fs_nearby_chat.xml
@@ -31,7 +31,7 @@
-
+
diff --git a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml
index 58f40fdf2a..36e192ee33 100644
--- a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml
+++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml
@@ -3,33 +3,26 @@
name="emojipicker"
title="Choose Emoji"
help_topic="emojipicker"
- positioning="cascading"
- legacy_header_height="0"
+ single_instance="true"
+ can_minimize="false"
+ can_tear_off="false"
can_resize="true"
+ auto_close="true"
layout="topleft"
min_width="250"
- height="400"
- width="250">
+ chrome="true"
+ height="350"
+ width="304">
-
+ height="275">
+ left="0"/>
+ height="25">
No emoji selected
+ height="25">No emoji selected
diff --git a/indra/newview/skins/default/xui/en/floater_fs_im_session.xml b/indra/newview/skins/default/xui/en/floater_fs_im_session.xml
index d3603a18a2..a77d5786b9 100644
--- a/indra/newview/skins/default/xui/en/floater_fs_im_session.xml
+++ b/indra/newview/skins/default/xui/en/floater_fs_im_session.xml
@@ -667,9 +667,11 @@
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
+ image_overlay="Emoji_Picker_Icon"
right="-11"
name="emoji_picker_toggle_btn"
- tool_tip="Show/hide emoji panel"
+ tab_stop="false"
+ tool_tip="Show emoji panel"
width="24"/>
@@ -728,6 +731,7 @@
follows="top|left|right"
layout="topleft"
max_visible="20"
+ tab_stop="false"
top="0"
left="5"
right="-1"
diff --git a/indra/newview/skins/default/xui/en/floater_fs_nearby_chat.xml b/indra/newview/skins/default/xui/en/floater_fs_nearby_chat.xml
index 169030f690..23e7e75819 100644
--- a/indra/newview/skins/default/xui/en/floater_fs_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/en/floater_fs_nearby_chat.xml
@@ -281,14 +281,15 @@
follows="right|bottom"
bottom="-1"
height="22"
- use_font_color="true"
font="EmojiLarge"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
+ image_overlay="Emoji_Picker_Icon"
right="-11"
name="emoji_picker_toggle_btn"
- tool_tip="Show/hide emoji panel"
+ tab_stop="false"
+ tool_tip="Show emoji panel"
width="24"/>
@@ -419,6 +421,7 @@
follows="top|left|right"
layout="topleft"
max_visible="20"
+ tab_stop="false"
top="0"
left="5"
right="-1"
diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml
index eb5d88bba6..c3e002e97d 100644
--- a/indra/newview/skins/default/xui/en/floater_im_session.xml
+++ b/indra/newview/skins/default/xui/en/floater_im_session.xml
@@ -304,11 +304,12 @@
name="emoji_recent_panel_toggle_btn"
tool_tip="Shows/hides recent emojis"
follows="right|bottom"
- use_font_color="true"
font="EmojiLarge"
+ tab_stop="false"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
+ image_overlay="Emoji_Picker_Icon"
bottom="-2"
right="-1"
height="25"
@@ -353,17 +354,19 @@
name="emoji_recent_icons_ctrl"
follows="top|left|right"
layout="topleft"
+ tab_stop="false"
max_visible="20"
top="0"
left="1"
right="-65"
height="30"/>