summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCallum Linden <113564339+callumlinden@users.noreply.github.com>2023-04-20 07:19:26 -0700
committerGitHub <noreply@github.com>2023-04-20 07:19:26 -0700
commita7eef57c1f3f8cd0850bd33b99f150d0e20d3e88 (patch)
tree09aeef796ca232ad1856d918c14eba34b3b4165c
parentdc5ef88e314d201f8f15be33d3517f45c4af2878 (diff)
parent97b0ba2a6d2596da867043077e32065653d44f6e (diff)
Merge pull request #183 from secondlife/SL-19575a
SL-19575 LLFloaterEmojiPicker - Add filter by category
-rw-r--r--indra/llcommon/llstring.h15
-rw-r--r--indra/llui/llbutton.cpp23
-rw-r--r--indra/llui/llbutton.h5
-rw-r--r--indra/llui/llemojidictionary.cpp10
-rw-r--r--indra/llui/llemojidictionary.h14
-rw-r--r--indra/llui/llfloater.cpp18
-rw-r--r--indra/llui/llfloater.h4
-rw-r--r--indra/llui/llscrolllistctrl.cpp4
-rw-r--r--indra/llui/llscrolllistctrl.h30
-rw-r--r--indra/newview/llfloateravatarpicker.cpp1
-rw-r--r--indra/newview/llfloateremojipicker.cpp203
-rw-r--r--indra/newview/llfloateremojipicker.h36
-rw-r--r--indra/newview/llfloaterimsessiontab.cpp13
-rw-r--r--indra/newview/llfloaterimsessiontab.h3
-rw-r--r--indra/newview/llviewerchat.cpp2
-rw-r--r--indra/newview/skins/default/xui/en/floater_emoji_picker.xml95
16 files changed, 368 insertions, 108 deletions
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 9afbea9afe..bdb90335e1 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -357,6 +357,7 @@ public:
static void replaceNonstandardASCII( string_type& string, T replacement );
static void replaceChar( string_type& string, T target, T replacement );
static void replaceString( string_type& string, string_type target, string_type replacement );
+ static void capitalize(string_type& str);
static BOOL containsNonprintable(const string_type& string);
static void stripNonprintable(string_type& string);
@@ -1596,6 +1597,20 @@ void LLStringUtilBase<T>::replaceTabsWithSpaces( string_type& str, size_type spa
}
//static
+template<class T>
+void LLStringUtilBase<T>::capitalize(string_type& str)
+{
+ if (str.size())
+ {
+ auto last = str[0] = toupper(str[0]);
+ for (U32 i = 1; i < str.size(); ++i)
+ {
+ last = (last == ' ' || last == '-' || last == '_') ? str[i] = toupper(str[i]) : str[i];
+ }
+ }
+}
+
+//static
template<class T>
BOOL LLStringUtilBase<T>::containsNonprintable(const string_type& string)
{
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 3354cb2db3..aeeff0b36f 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -68,6 +68,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),
image_unselected("image_unselected"),
image_selected("image_selected"),
image_hover_selected("image_hover_selected"),
@@ -160,6 +161,7 @@ LLButton::LLButton(const LLButton::Params& p)
mDropShadowedText(p.label_shadow),
mAutoResize(p.auto_resize),
mUseEllipses( p.use_ellipses ),
+ mUseFontColor( p.use_font_color),
mHAlign(p.font_halign),
mLeftHPad(p.pad_left),
mRightHPad(p.pad_right),
@@ -960,7 +962,7 @@ void LLButton::draw()
LLFontGL::NORMAL,
mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
S32_MAX, text_width,
- NULL, mUseEllipses);
+ NULL, mUseEllipses, mUseFontColor);
}
LLUICtrl::draw();
@@ -1020,6 +1022,16 @@ BOOL LLButton::toggleState()
return flipped;
}
+void LLButton::setLabel( const std::string& label )
+{
+ mUnselectedLabel = mSelectedLabel = label;
+}
+
+void LLButton::setLabel( const LLUIString& label )
+{
+ mUnselectedLabel = mSelectedLabel = label;
+}
+
void LLButton::setLabel( const LLStringExplicit& label )
{
setLabelUnselected(label);
@@ -1051,14 +1063,7 @@ bool LLButton::labelIsTruncated() const
const LLUIString& LLButton::getCurrentLabel() const
{
- if( getToggleState() )
- {
- return mSelectedLabel;
- }
- else
- {
- return mUnselectedLabel;
- }
+ return getToggleState() ? mSelectedLabel : mUnselectedLabel;
}
void LLButton::setImageUnselected(LLPointer<LLUIImage> image)
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index ccd31e90c0..257159f64f 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -73,6 +73,7 @@ public:
Optional<bool> label_shadow;
Optional<bool> auto_resize;
Optional<bool> use_ellipses;
+ Optional<bool> use_font_color;
// images
Optional<LLUIImage*> image_unselected,
@@ -174,6 +175,7 @@ public:
void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; }
void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; }
void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; }
+ void setUseFontColor( BOOL use_font_color) { mUseFontColor = use_font_color; }
boost::signals2::connection setClickedCallback(const CommitCallbackParam& cb);
@@ -238,6 +240,8 @@ public:
void autoResize(); // resize with label of current btn state
void resize(LLUIString label); // resize with label input
+ void setLabel(const std::string& label);
+ void setLabel(const LLUIString& label);
void setLabel( const LLStringExplicit& label);
virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text );
void setLabelUnselected(const LLStringExplicit& label);
@@ -353,6 +357,7 @@ protected:
bool mDropShadowedText;
bool mAutoResize;
bool mUseEllipses;
+ bool mUseFontColor;
bool mBorderEnabled;
bool mFlashing;
diff --git a/indra/llui/llemojidictionary.cpp b/indra/llui/llemojidictionary.cpp
index b70a9b2e7a..d306407484 100644
--- a/indra/llui/llemojidictionary.cpp
+++ b/indra/llui/llemojidictionary.cpp
@@ -175,6 +175,12 @@ LLWString LLEmojiDictionary::findMatchingEmojis(const std::string& needle) const
return result;
}
+const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromEmoji(llwchar emoji) const
+{
+ const auto it = mEmoji2Descr.find(emoji);
+ return (mEmoji2Descr.end() != it) ? it->second : nullptr;
+}
+
const LLEmojiDescriptor* LLEmojiDictionary::getDescriptorFromShortCode(const std::string& short_code) const
{
const auto it = mShortCode2Descr.find(short_code);
@@ -195,6 +201,10 @@ void LLEmojiDictionary::addEmoji(LLEmojiDescriptor&& descr)
{
mShortCode2Descr.insert(std::make_pair(shortCode, &mEmojis.back()));
}
+ for (const std::string& category : descr.Categories)
+ {
+ mCategory2Descrs[category].push_back(&mEmojis.back());
+ }
}
// ============================================================================
diff --git a/indra/llui/llemojidictionary.h b/indra/llui/llemojidictionary.h
index adc22ced58..cbb0ac577d 100644
--- a/indra/llui/llemojidictionary.h
+++ b/indra/llui/llemojidictionary.h
@@ -56,20 +56,28 @@ class LLEmojiDictionary : public LLParamSingleton<LLEmojiDictionary>, public LLI
~LLEmojiDictionary() override {};
public:
+ typedef std::map<llwchar, const LLEmojiDescriptor*> emoji2descr_map_t;
+ typedef std::map<std::string, const LLEmojiDescriptor*> code2descr_map_t;
+ typedef std::map<std::string, std::vector<const LLEmojiDescriptor*>> cat2descrs_map_t;
+
static void initClass();
LLWString findMatchingEmojis(const std::string& needle) const;
+ const LLEmojiDescriptor* getDescriptorFromEmoji(llwchar emoji) const;
const LLEmojiDescriptor* getDescriptorFromShortCode(const std::string& short_code) const;
std::string getNameFromEmoji(llwchar ch) const;
- const std::map<llwchar, const LLEmojiDescriptor*>& getEmoji2Descr() const { return mEmoji2Descr; }
+ const emoji2descr_map_t& getEmoji2Descr() const { return mEmoji2Descr; }
+ const code2descr_map_t& getShortCode2Descr() const { return mShortCode2Descr; }
+ const cat2descrs_map_t& getCategory2Descrs() const { return mCategory2Descrs; }
private:
void addEmoji(LLEmojiDescriptor&& descr);
private:
std::list<LLEmojiDescriptor> mEmojis;
- std::map<llwchar, const LLEmojiDescriptor*> mEmoji2Descr;
- std::map<std::string, const LLEmojiDescriptor*> mShortCode2Descr;
+ emoji2descr_map_t mEmoji2Descr;
+ code2descr_map_t mShortCode2Descr;
+ cat2descrs_map_t mCategory2Descrs;
};
// ============================================================================
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 346e89a101..98895d56dd 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -1509,14 +1509,24 @@ BOOL LLFloater::isFrontmost()
&& floater_view->getFrontmost() == this);
}
-void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
+void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition, BOOL resize)
{
mDependents.insert(floaterp->getHandle());
floaterp->mDependeeHandle = getHandle();
if (reposition)
{
- floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp));
+ LLRect rect = gFloaterView->findNeighboringPosition(this, floaterp);
+ if (resize)
+ {
+ const LLRect& base = getRect();
+ if (rect.mTop == base.mTop)
+ rect.mBottom = base.mBottom;
+ else if (rect.mLeft == base.mLeft)
+ rect.mRight = base.mRight;
+ floaterp->reshape(rect.getWidth(), rect.getHeight(), FALSE);
+ }
+ floaterp->setRect(rect);
floaterp->setSnapTarget(getHandle());
}
gFloaterView->adjustToFitScreen(floaterp, FALSE, TRUE);
@@ -1527,12 +1537,12 @@ void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
}
}
-void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, BOOL reposition)
+void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, BOOL reposition, BOOL resize)
{
LLFloater* dependent_floaterp = dependent.get();
if(dependent_floaterp)
{
- addDependentFloater(dependent_floaterp, reposition);
+ addDependentFloater(dependent_floaterp, reposition, resize);
}
}
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 282f7a80ac..3699629ef8 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -255,8 +255,8 @@ public:
std::string getShortTitle() const;
virtual void setMinimized(BOOL b);
void moveResizeHandlesToFront();
- void addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE);
- void addDependentFloater(LLHandle<LLFloater> dependent_handle, BOOL reposition = TRUE);
+ void addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE, BOOL resize = FALSE);
+ void addDependentFloater(LLHandle<LLFloater> dependent_handle, BOOL reposition = TRUE, BOOL resize = FALSE);
LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); }
void removeDependentFloater(LLFloater* dependent);
BOOL isMinimized() const { return mMinimized; }
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 2a6e8a6b76..f982dc99e8 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -1269,7 +1269,7 @@ BOOL LLScrollListCtrl::selectItemByLabel(const std::string& label, BOOL case_sen
LLScrollListItem* item = getItemByLabel(label, case_sensitive, column);
bool found = NULL != item;
- if(found)
+ if (found)
{
selectItem(item, -1);
}
@@ -2747,7 +2747,7 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending)
S32 LLScrollListCtrl::getLinesPerPage()
{
//if mPageLines is NOT provided display all item
- if(mPageLines)
+ if (mPageLines)
{
return mPageLines;
}
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 73b4fb036a..326589a329 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -253,7 +253,7 @@ public:
S32 getItemIndex( LLScrollListItem* item ) const;
S32 getItemIndex( const LLUUID& item_id ) const;
- void setCommentText( const std::string& comment_text);
+ void setCommentText( const std::string& comment_text);
LLScrollListItem* addSeparator(EAddPosition pos);
// "Simple" interface: use this when you're creating a list that contains only unique strings, only
@@ -263,7 +263,7 @@ public:
BOOL selectItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 ); // FALSE if item not found
BOOL selectItemByPrefix(const std::string& target, BOOL case_sensitive = TRUE, S32 column = -1);
BOOL selectItemByPrefix(const LLWString& target, BOOL case_sensitive = TRUE, S32 column = -1);
- LLScrollListItem* getItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 );
+ LLScrollListItem* getItemByLabel(const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0);
const std::string getSelectedItemLabel(S32 column = 0) const;
LLSD getSelectedValue();
@@ -322,7 +322,7 @@ public:
virtual S32 getScrollPos() const;
virtual void setScrollPos( S32 pos );
- S32 getSearchColumn();
+ S32 getSearchColumn();
void setSearchColumn(S32 column) { mSearchColumn = column; }
S32 getColumnIndexFromOffset(S32 x);
S32 getColumnOffsetFromIndex(S32 index);
@@ -371,13 +371,13 @@ public:
// Used "internally" by the scroll bar.
void onScrollChange( S32 new_pos, LLScrollbar* src );
- static void onClickColumn(void *userdata);
+ static void onClickColumn(void *userdata);
- virtual void updateColumns(bool force_update = false);
- S32 calcMaxContentWidth();
- bool updateColumnWidths();
+ virtual void updateColumns(bool force_update = false);
+ S32 calcMaxContentWidth();
+ bool updateColumnWidths();
- void setHeadingHeight(S32 heading_height);
+ void setHeadingHeight(S32 heading_height);
/**
* Sets max visible lines without scroolbar, if this value equals to 0,
* then display all items.
@@ -398,18 +398,20 @@ public:
virtual void deselect();
virtual BOOL canDeselect() const;
- void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; }
- void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width);
- S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }
+ void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; }
+ void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width);
+ S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }
std::string getSortColumnName();
BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
BOOL hasSortOrder() const;
void clearSortOrder();
- void setAlternateSort() { mAlternateSort = true; }
+ void setAlternateSort() { mAlternateSort = TRUE; }
- S32 selectMultiple( uuid_vec_t ids );
+ void selectPrevItem(BOOL extend_selection = FALSE);
+ void selectNextItem(BOOL extend_selection = FALSE);
+ S32 selectMultiple(uuid_vec_t ids);
// conceptually const, but mutates mItemList
void updateSort() const;
// sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example)
@@ -454,8 +456,6 @@ protected:
void updateLineHeight();
private:
- void selectPrevItem(BOOL extend_selection);
- void selectNextItem(BOOL extend_selection);
void drawItems();
void updateLineHeightInsert(LLScrollListItem* item);
diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp
index 2422596f60..42ef41017a 100644
--- a/indra/newview/llfloateravatarpicker.cpp
+++ b/indra/newview/llfloateravatarpicker.cpp
@@ -741,7 +741,6 @@ void LLFloaterAvatarPicker::processResponse(const LLUUID& query_id, const LLSD&
}
}
-//static
void LLFloaterAvatarPicker::editKeystroke(LLLineEditor* caller, void* user_data)
{
getChildView("Find")->setEnabled(caller->getText().size() > 0);
diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp
index a63a9fac4f..5efd2a24c0 100644
--- a/indra/newview/llfloateremojipicker.cpp
+++ b/indra/newview/llfloateremojipicker.cpp
@@ -27,10 +27,18 @@
#include "llfloateremojipicker.h"
+#include "llcombobox.h"
+#include "llemojidictionary.h"
#include "llfloaterreg.h"
+#include "lllineeditor.h"
#include "llscrolllistctrl.h"
#include "llscrolllistitem.h"
-#include "llemojidictionary.h"
+#include "lltextbox.h"
+#include "llviewerchat.h"
+
+std::string LLFloaterEmojiPicker::mSelectedCategory;
+std::string LLFloaterEmojiPicker::mSearchPattern;
+int LLFloaterEmojiPicker::mSelectedEmojiIndex;
class LLEmojiScrollListItem : public LLScrollListItem
{
@@ -69,17 +77,18 @@ LLFloaterEmojiPicker* LLFloaterEmojiPicker::getInstance()
return floater;
}
-LLFloaterEmojiPicker* LLFloaterEmojiPicker::showInstance(select_callback_t callback)
+LLFloaterEmojiPicker* LLFloaterEmojiPicker::showInstance(pick_callback_t pick_callback, close_callback_t close_callback)
{
LLFloaterEmojiPicker* floater = getInstance();
if (LLFloaterEmojiPicker* floater = getInstance())
- floater->show(callback);
+ floater->show(pick_callback, close_callback);
return floater;
}
-void LLFloaterEmojiPicker::show(select_callback_t callback)
+void LLFloaterEmojiPicker::show(pick_callback_t pick_callback, close_callback_t close_callback)
{
- mSelectCallback = callback;
+ mEmojiPickCallback = pick_callback;
+ mFloaterCloseCallback = close_callback;
openFloater(mKey);
setFocus(TRUE);
}
@@ -91,19 +100,41 @@ LLFloaterEmojiPicker::LLFloaterEmojiPicker(const LLSD& key)
BOOL LLFloaterEmojiPicker::postBuild()
{
- if ((mEmojis = getChild<LLScrollListCtrl>("Emojis")))
+ // Should be initialized first
+ if ((mPreviewEmoji = getChild<LLButton>("PreviewEmoji")))
{
- mEmojis->setDoubleClickCallback(boost::bind(&LLFloaterEmojiPicker::onSelect, this));
-
- mEmojis->clearRows();
+ mPreviewEmoji->setClickedCallback(boost::bind(&LLFloaterEmojiPicker::onPreviewEmojiClick, this));
+ }
- const std::map<llwchar, const LLEmojiDescriptor*>& emoji2Descr = LLEmojiDictionary::instance().getEmoji2Descr();
- for (const std::pair<const llwchar, const LLEmojiDescriptor*>& it : emoji2Descr)
+ if ((mCategory = getChild<LLComboBox>("Category")))
+ {
+ mCategory->setCommitCallback(boost::bind(&LLFloaterEmojiPicker::onCategoryCommit, this));
+ mCategory->setLabel(LLStringExplicit("Choose a category"));
+ const auto& cat2Descrs = LLEmojiDictionary::instance().getCategory2Descrs();
+ mCategory->clearRows();
+ for (const auto& item : cat2Descrs)
{
- LLScrollListItem::Params params;
- params.columns.add().column("name").value(it.second->Name);
- mEmojis->addRow(new LLEmojiScrollListItem(it.first, params), params);
+ std::string value = item.first;
+ std::string name = value;
+ LLStringUtil::capitalize(name);
+ mCategory->add(name, value);
}
+ mCategory->setSelectedByValue(mSelectedCategory, true);
+ }
+
+ if ((mSearch = getChild<LLLineEditor>("Search")))
+ {
+ mSearch->setKeystrokeCallback(boost::bind(&LLFloaterEmojiPicker::onSearchKeystroke, this, _1, _2), NULL);
+ mSearch->setLabel(LLStringExplicit("Type to search an emoji"));
+ mSearch->setFont(LLViewerChat::getChatFont());
+ mSearch->setText(mSearchPattern);
+ }
+
+ if ((mEmojis = getChild<LLScrollListCtrl>("Emojis")))
+ {
+ mEmojis->setCommitCallback(boost::bind(&LLFloaterEmojiPicker::onEmojiSelect, this));
+ mEmojis->setDoubleClickCallback(boost::bind(&LLFloaterEmojiPicker::onEmojiPick, this));
+ fillEmojis();
}
return TRUE;
@@ -114,30 +145,152 @@ LLFloaterEmojiPicker::~LLFloaterEmojiPicker()
gFocusMgr.releaseFocusIfNeeded( this );
}
-void LLFloaterEmojiPicker::onSelect()
+void LLFloaterEmojiPicker::fillEmojis()
+{
+ mEmojis->clearRows();
+
+ const auto& emoji2Descr = LLEmojiDictionary::instance().getEmoji2Descr();
+ for (const std::pair<const llwchar, const LLEmojiDescriptor*>& it : emoji2Descr)
+ {
+ const LLEmojiDescriptor* descr = it.second;
+
+ if (!mSelectedCategory.empty() && !matchesCategory(descr))
+ continue;
+
+ if (!mSearchPattern.empty() && !matchesPattern(descr))
+ continue;
+
+ LLScrollListItem::Params params;
+ params.columns.add().column("name").value(descr->Name);
+ mEmojis->addRow(new LLEmojiScrollListItem(it.first, params), params);
+ }
+
+ if (mEmojis->getItemCount())
+ {
+ if (mSelectedEmojiIndex > 0 && mSelectedEmojiIndex < mEmojis->getItemCount())
+ mEmojis->selectNthItem(mSelectedEmojiIndex);
+ else
+ mEmojis->selectFirstItem();
+
+ mEmojis->scrollToShowSelected();
+ }
+ else
+ {
+ onEmojiEmpty();
+ }
+}
+
+bool LLFloaterEmojiPicker::matchesCategory(const LLEmojiDescriptor* descr)
+{
+ return std::find(descr->Categories.begin(), descr->Categories.end(), mSelectedCategory) != descr->Categories.end();
+}
+
+bool LLFloaterEmojiPicker::matchesPattern(const LLEmojiDescriptor* descr)
+{
+ if (descr->Name.find(mSearchPattern) != std::string::npos)
+ return true;
+ for (auto shortCode : descr->ShortCodes)
+ if (shortCode.find(mSearchPattern) != std::string::npos)
+ return true;
+ for (auto category : descr->Categories)
+ if (category.find(mSearchPattern) != std::string::npos)
+ return true;
+ return false;
+}
+
+void LLFloaterEmojiPicker::onCategoryCommit()
+{
+ mSelectedCategory = mCategory->getSelectedValue().asString();
+ mSelectedEmojiIndex = 0;
+ fillEmojis();
+}
+
+void LLFloaterEmojiPicker::onSearchKeystroke(LLLineEditor* caller, void* user_data)
{
- if (mEmojis && mSelectCallback)
+ mSearchPattern = mSearch->getText();
+ mSelectedEmojiIndex = 0;
+ fillEmojis();
+}
+
+void LLFloaterEmojiPicker::onPreviewEmojiClick()
+{
+ if (mEmojis && mEmojiPickCallback)
{
if (LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojis->getFirstSelected()))
{
- mSelectCallback(item->getEmoji());
+ mEmojiPickCallback(item->getEmoji());
}
}
}
-// virtual
-BOOL LLFloaterEmojiPicker::handleKeyHere(KEY key, MASK mask)
+void LLFloaterEmojiPicker::onEmojiSelect()
{
- if (key == KEY_RETURN && mask == MASK_NONE)
+ const LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojis->getFirstSelected());
+ if (item)
{
- onSelect();
- return TRUE;
+ mSelectedEmojiIndex = mEmojis->getFirstSelectedIndex();
+ LLUIString text;
+ text.insert(0, LLWString(1, item->getEmoji()));
+ if (mPreviewEmoji)
+ mPreviewEmoji->setLabel(text);
+ return;
}
- else if (key == KEY_ESCAPE && mask == MASK_NONE)
+
+ onEmojiEmpty();
+}
+
+void LLFloaterEmojiPicker::onEmojiEmpty()
+{
+ mSelectedEmojiIndex = 0;
+ if (mPreviewEmoji)
+ mPreviewEmoji->setLabel(LLUIString());
+}
+
+void LLFloaterEmojiPicker::onEmojiPick()
+{
+ if (mEmojis && mEmojiPickCallback)
{
- closeFloater();
- return TRUE;
+ if (LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojis->getFirstSelected()))
+ {
+ mEmojiPickCallback(item->getEmoji());
+ closeFloater();
+ }
+ }
+}
+
+// virtual
+BOOL LLFloaterEmojiPicker::handleKeyHere(KEY key, MASK mask)
+{
+ if (mask == MASK_NONE)
+ {
+ switch (key)
+ {
+ case KEY_RETURN:
+ if (mCategory->hasFocus())
+ break;
+ onEmojiPick();
+ return TRUE;
+ case KEY_ESCAPE:
+ closeFloater();
+ return TRUE;
+ case KEY_UP:
+ mEmojis->selectPrevItem();
+ mEmojis->scrollToShowSelected();
+ return TRUE;
+ case KEY_DOWN:
+ mEmojis->selectNextItem();
+ mEmojis->scrollToShowSelected();
+ return TRUE;
+ }
}
return LLFloater::handleKeyHere(key, mask);
}
+
+// virtual
+void LLFloaterEmojiPicker::closeFloater(bool app_quitting)
+{
+ LLFloater::closeFloater(app_quitting);
+ if (mFloaterCloseCallback)
+ mFloaterCloseCallback();
+}
diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h
index 9b442064d0..01335bbb5b 100644
--- a/indra/newview/llfloateremojipicker.h
+++ b/indra/newview/llfloateremojipicker.h
@@ -29,31 +29,53 @@
#include "llfloater.h"
+struct LLEmojiDescriptor;
+
class LLFloaterEmojiPicker : public LLFloater
{
public:
// The callback function will be called with an emoji char.
- typedef boost::function<void (llwchar)> select_callback_t;
+ typedef boost::function<void (llwchar)> pick_callback_t;
+ typedef boost::function<void ()> close_callback_t;
// Call this to select an emoji.
static LLFloaterEmojiPicker* getInstance();
- static LLFloaterEmojiPicker* showInstance(select_callback_t callback);
+ static LLFloaterEmojiPicker* showInstance(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr);
LLFloaterEmojiPicker(const LLSD& key);
virtual ~LLFloaterEmojiPicker();
virtual BOOL postBuild();
- void show(select_callback_t callback);
+ void show(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr);
+
+ virtual void closeFloater(bool app_quitting = false);
private:
- void onSelect();
+ void fillEmojis();
+ bool matchesCategory(const LLEmojiDescriptor* descr);
+ bool matchesPattern(const LLEmojiDescriptor* descr);
+
+ void onCategoryCommit();
+ void onSearchKeystroke(class LLLineEditor* caller, void* user_data);
+ void onPreviewEmojiClick();
+ void onEmojiSelect();
+ void onEmojiEmpty();
+ void onEmojiPick();
virtual BOOL handleKeyHere(KEY key, MASK mask);
- class LLScrollListCtrl* mEmojis;
- select_callback_t mSelectCallback;
- std::string mEmojiName;
+ class LLComboBox* mCategory { nullptr };
+ class LLLineEditor* mSearch { nullptr };
+ class LLScrollListCtrl* mEmojis { nullptr };
+ class LLButton* mPreviewEmoji { nullptr };
+
+ pick_callback_t mEmojiPickCallback;
+ close_callback_t mFloaterCloseCallback;
+
+ static std::string mSelectedCategory;
+ static std::string mSearchPattern;
+ static int mSelectedEmojiIndex;
};
#endif
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
index 3d9751dd35..0571a0d855 100644
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -434,10 +434,12 @@ void LLFloaterIMSessionTab::onEmojiPanelBtnClicked(LLFloaterIMSessionTab* self)
{
if (!picker->isShown())
{
- picker->show(boost::bind(&LLFloaterIMSessionTab::onEmojiSelected, self, _1));
+ picker->show(
+ boost::bind(&LLFloaterIMSessionTab::onEmojiPicked, self, _1),
+ boost::bind(&LLFloaterIMSessionTab::onEmojiPickerClosed, self));
if (LLFloater* root_floater = gFloaterView->getParentFloater(self))
{
- root_floater->addDependentFloater(picker);
+ root_floater->addDependentFloater(picker, TRUE, TRUE);
}
}
else
@@ -447,11 +449,16 @@ void LLFloaterIMSessionTab::onEmojiPanelBtnClicked(LLFloaterIMSessionTab* self)
}
}
-void LLFloaterIMSessionTab::onEmojiSelected(llwchar emoji)
+void LLFloaterIMSessionTab::onEmojiPicked(llwchar emoji)
{
mInputEditor->insertEmoji(emoji);
}
+void LLFloaterIMSessionTab::onEmojiPickerClosed()
+{
+ mInputEditor->setFocus(TRUE);
+}
+
std::string LLFloaterIMSessionTab::appendTime()
{
time_t utc_time;
diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h
index cd5065420d..3dcb767aa6 100644
--- a/indra/newview/llfloaterimsessiontab.h
+++ b/indra/newview/llfloaterimsessiontab.h
@@ -208,7 +208,8 @@ private:
void onInputEditorClicked();
static void onEmojiPanelBtnClicked(LLFloaterIMSessionTab* self);
- void onEmojiSelected(llwchar emoji);
+ void onEmojiPicked(llwchar emoji);
+ void onEmojiPickerClosed();
bool checkIfTornOff();
bool mIsHostAttached;
diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp
index 1c3c547bc1..0d2d62fd77 100644
--- a/indra/newview/llviewerchat.cpp
+++ b/indra/newview/llviewerchat.cpp
@@ -25,7 +25,7 @@
*/
#include "llviewerprecompiledheaders.h"
-#include "llviewerchat.h"
+#include "llviewerchat.h"
// newview includes
#include "llagent.h" // gAgent
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 a3e504cc31..831f7b6582 100644
--- a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml
+++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml
@@ -1,39 +1,64 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
- name="emojipicker"
- title="CHOOSE EMOJI"
- help_topic="emojipicker"
- positioning="cascading"
- legacy_header_height="10"
- can_resize="false"
- layout="topleft"
- height="419"
- width="200">
- <panel
- label="Emojis"
- name="EmojiPanel"
- help_topic="emojipicker"
+ name="emojipicker"
+ title="CHOOSE EMOJI"
+ help_topic="emojipicker"
+ positioning="cascading"
+ legacy_header_height="0"
+ can_resize="true"
+ layout="topleft"
+ height="400"
+ width="200">
+ <line_editor
+ name="Search"
+ layout="bottomleft"
+ follows="bottom|left|right"
+ text_tentative_color="TextFgTentativeColor"
+ max_length_bytes="63"
+ left_pad="5"
+ bottom="14"
+ left="34"
+ height="29"
+ width="162" />
+ <button
+ name="PreviewEmoji"
+ layout="bottomleft"
+ follows="bottom|left"
+ font="EmojiHuge"
+ use_font_color="true"
+ bottom="14"
+ left="0"
+ height="29"
+ width="29" />
+ <scroll_list
+ name="Emojis"
layout="topleft"
- top="4"
- left="2"
- height="410"
- width="196">
- <scroll_list
- draw_heading="true"
- heading_height="28"
- follows="all"
- layout="topleft"
- name="Emojis"
- sort_column="0"
- height="410">
- <columns
- label="Look"
- name="look"
- width="40" />
- <columns
- label="Name"
- name="name"
- width="150" />
- </scroll_list>
- </panel>
+ follows="all"
+ sort_column="0"
+ max_chars="63"
+ commit_on_selection_change="true"
+ draw_heading="true"
+ heading_height="25"
+ row_padding="0"
+ top="25"
+ height="330"
+ width="200">
+ <columns
+ label="Look"
+ name="look"
+ width="40" />
+ <columns
+ label="Name"
+ name="name" />
+ </scroll_list>
+ <combo_box
+ name="Category"
+ layout="topleft"
+ follows="top|left|right"
+ allow_text_entry="true"
+ left_pad="4"
+ top="0"
+ left="4"
+ height="27"
+ width="192" />
</floater>