diff options
author | Callum Linden <113564339+callumlinden@users.noreply.github.com> | 2023-04-20 07:19:26 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-20 07:19:26 -0700 |
commit | a7eef57c1f3f8cd0850bd33b99f150d0e20d3e88 (patch) | |
tree | 09aeef796ca232ad1856d918c14eba34b3b4165c | |
parent | dc5ef88e314d201f8f15be33d3517f45c4af2878 (diff) | |
parent | 97b0ba2a6d2596da867043077e32065653d44f6e (diff) |
Merge pull request #183 from secondlife/SL-19575a
SL-19575 LLFloaterEmojiPicker - Add filter by category
-rw-r--r-- | indra/llcommon/llstring.h | 15 | ||||
-rw-r--r-- | indra/llui/llbutton.cpp | 23 | ||||
-rw-r--r-- | indra/llui/llbutton.h | 5 | ||||
-rw-r--r-- | indra/llui/llemojidictionary.cpp | 10 | ||||
-rw-r--r-- | indra/llui/llemojidictionary.h | 14 | ||||
-rw-r--r-- | indra/llui/llfloater.cpp | 18 | ||||
-rw-r--r-- | indra/llui/llfloater.h | 4 | ||||
-rw-r--r-- | indra/llui/llscrolllistctrl.cpp | 4 | ||||
-rw-r--r-- | indra/llui/llscrolllistctrl.h | 30 | ||||
-rw-r--r-- | indra/newview/llfloateravatarpicker.cpp | 1 | ||||
-rw-r--r-- | indra/newview/llfloateremojipicker.cpp | 203 | ||||
-rw-r--r-- | indra/newview/llfloateremojipicker.h | 36 | ||||
-rw-r--r-- | indra/newview/llfloaterimsessiontab.cpp | 13 | ||||
-rw-r--r-- | indra/newview/llfloaterimsessiontab.h | 3 | ||||
-rw-r--r-- | indra/newview/llviewerchat.cpp | 2 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_emoji_picker.xml | 95 |
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> |