diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index bafeef41fb..cfaf08ec0a 100644 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -178,6 +178,10 @@ void LLSearchEditor::setFocus( BOOL b ) void LLSearchEditor::onClearButtonClick(const LLSD& data) { setText(LLStringUtil::null); + if (mTextChangedCallback) + { + mTextChangedCallback(this, getValue()); + } mSearchEditor->onCommit(); // force keystroke callback } diff --git a/indra/newview/fscommon.cpp b/indra/newview/fscommon.cpp index d5bb19e095..069e37dd3a 100644 --- a/indra/newview/fscommon.cpp +++ b/indra/newview/fscommon.cpp @@ -33,6 +33,8 @@ #include "llagentbenefits.h" #include "llavataractions.h" #include "llavatarnamecache.h" +#include "llemojidictionary.h" +#include "llfloateremojipicker.h" #include "llfloaterperms.h" #include "llgroupactions.h" #include "llgroupmgr.h" @@ -519,3 +521,16 @@ LLUUID FSCommon::getGroupForRezzing() return group_id; } + +void FSCommon::updateUsedEmojis(LLWString text) +{ + LLEmojiDictionary* dictionary = LLEmojiDictionary::getInstance(); + + for (llwchar& c : text) + { + if (dictionary->isEmoji(c)) + { + LLFloaterEmojiPicker::onEmojiUsed(c); + } + } +} \ No newline at end of file diff --git a/indra/newview/fscommon.h b/indra/newview/fscommon.h index 4653b1a402..0cf2196313 100644 --- a/indra/newview/fscommon.h +++ b/indra/newview/fscommon.h @@ -93,6 +93,8 @@ namespace FSCommon bool isFilterEditorKeyCombo(KEY key, MASK mask); LLUUID getGroupForRezzing(); + + void updateUsedEmojis(LLWString text); }; #endif // FS_COMMON_H diff --git a/indra/newview/fsfloaterim.cpp b/indra/newview/fsfloaterim.cpp index 4c6bb5efc2..0a798d7607 100644 --- a/indra/newview/fsfloaterim.cpp +++ b/indra/newview/fsfloaterim.cpp @@ -64,6 +64,7 @@ #include "llnotifications.h" #include "llnotificationsutil.h" #include "llnotificationtemplate.h" // Viewer version popup +#include "llpanelemojicomplete.h" #include "llrootview.h" #include "llscreenchannel.h" #include "llspeakers.h" @@ -376,6 +377,8 @@ void FSFloaterIM::sendMsgFromInputEditor(EChatType type) LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if (!text.empty()) { + FSCommon::updateUsedEmojis(text); + if (type == CHAT_TYPE_OOC) { std::string tempText = wstring_to_utf8str( text ); @@ -950,9 +953,22 @@ BOOL FSFloaterIM::postBuild() mInputEditor->enableSingleLineMode(gSavedSettings.getBOOL("FSUseSingleLineChatEntry")); mInputEditor->setCommitCallback(boost::bind(&FSFloaterIM::sendMsgFromInputEditor, this, CHAT_TYPE_NORMAL)); - mEmojiButton = getChild("emoji_panel_btn"); - mEmojiButton->setLabel(LLUIString(LLWString(1, 128512))); - mEmojiButton->setClickedCallback(boost::bind(&FSFloaterIM::onEmojiPanelBtnClicked, this)); + mEmojiRecentPanelToggleBtn = getChild("emoji_recent_panel_toggle_btn"); + mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(this); }); + + mEmojiRecentPanel = getChild("emoji_recent_layout_panel"); + mEmojiRecentPanel->setVisible(false); + + mEmojiRecentEmptyText = getChildView("emoji_recent_empty_text"); + mEmojiRecentEmptyText->setVisible(false); + + mEmojiRecentIconsCtrl = getChild("emoji_recent_icons_ctrl"); + mEmojiRecentIconsCtrl->setCommitCallback([this](LLUICtrl*, const LLSD& value) { onRecentEmojiPicked(value); }); + mEmojiRecentIconsCtrl->setVisible(false); + + mEmojiPickerToggleBtn = getChild("emoji_picker_toggle_btn"); + mEmojiPickerToggleBtn->setLabel(LLUIString(LLWString(1, 128512))); + mEmojiPickerToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerToggleBtnClicked(this); }); getChild("send_chat")->setCommitCallback(boost::bind(&FSFloaterIM::sendMsgFromInputEditor, this, CHAT_TYPE_NORMAL)); getChild("chat_search_btn")->setCommitCallback(boost::bind(&FSFloaterIM::onChatSearchButtonClicked, this)); @@ -2448,7 +2464,54 @@ bool FSFloaterIM::applyRectControl() return res; } -void FSFloaterIM::onEmojiPanelBtnClicked(FSFloaterIM* self) +void FSFloaterIM::onEmojiRecentPanelToggleBtnClicked(FSFloaterIM* self) +{ + BOOL show = !self->mEmojiRecentPanel->getVisible(); + if (show) + { + self->onEmojiRecentPanelOpening(); + } + self->mEmojiRecentPanel->setVisible(show); + self->mEmojiRecentPanelToggleBtn->setImageOverlay(show ? "Arrow_Up" : "Arrow_Down"); +} + +void FSFloaterIM::onEmojiRecentPanelOpening() +{ + std::list& recentlyUsed = LLFloaterEmojiPicker::getRecentlyUsed(); + if (recentlyUsed.empty()) + { + mEmojiRecentEmptyText->setVisible(true); + mEmojiRecentIconsCtrl->setVisible(false); + } + else + { + LLWString emojis; + for (llwchar emoji : recentlyUsed) + { + emojis += emoji; + } + mEmojiRecentIconsCtrl->setEmojis(emojis); + mEmojiRecentEmptyText->setVisible(false); + mEmojiRecentIconsCtrl->setVisible(true); + mEmojiRecentIconsCtrl->setFocus(true); + } +} + +void FSFloaterIM::onRecentEmojiPicked(const LLSD& value) +{ + LLSD::String str = value.asString(); + if (str.size()) + { + LLWString wstr = utf8string_to_wstring(str); + if (wstr.size()) + { + llwchar emoji = wstr[0]; + onEmojiPicked(emoji); + } + } +} + +void FSFloaterIM::onEmojiPickerToggleBtnClicked(FSFloaterIM* self) { if (LLFloaterEmojiPicker* picker = LLFloaterEmojiPicker::getInstance()) { diff --git a/indra/newview/fsfloaterim.h b/indra/newview/fsfloaterim.h index 0dd4977734..ec30f145c8 100644 --- a/indra/newview/fsfloaterim.h +++ b/indra/newview/fsfloaterim.h @@ -47,10 +47,10 @@ class LLInventoryCategory; class LLInventoryItem; class LLLayoutPanel; class LLLayoutStack; +class LLPanelEmojiComplete; class LLTextBox; class LLTextEditor; - typedef boost::signals2::signal floater_showed_signal_t; /** @@ -239,7 +239,10 @@ private: bool onChatOptionsVisibleContextMenuItem(const LLSD& userdata); bool onChatOptionsEnableContextMenuItem(const LLSD& userdata); - static void onEmojiPanelBtnClicked(FSFloaterIM* self); + static void onEmojiRecentPanelToggleBtnClicked(FSFloaterIM* self); + static void onEmojiPickerToggleBtnClicked(FSFloaterIM* self); + void onEmojiRecentPanelOpening(); + void onRecentEmojiPicked(const LLSD& value); void onEmojiPicked(llwchar emoji); void onEmojiPickerClosed(); @@ -256,7 +259,11 @@ private: LLLayoutStack* mInputPanels; LLLayoutPanel* mUnreadMessagesNotificationPanel; LLTextBox* mUnreadMessagesNotificationTextBox; - LLButton* mEmojiButton; + LLButton* mEmojiRecentPanelToggleBtn; + LLButton* mEmojiPickerToggleBtn; + LLLayoutPanel* mEmojiRecentPanel; + LLView* mEmojiRecentEmptyText; + LLPanelEmojiComplete* mEmojiRecentIconsCtrl; std::string mSavedTitle; LLUIString mTypingStart; diff --git a/indra/newview/fsfloaternearbychat.cpp b/indra/newview/fsfloaternearbychat.cpp index a022a12245..2de4df2a17 100644 --- a/indra/newview/fsfloaternearbychat.cpp +++ b/indra/newview/fsfloaternearbychat.cpp @@ -61,6 +61,7 @@ #include "lllogchat.h" #include "llmenugl.h" #include "llmultigesture.h" +#include "llpanelemojicomplete.h" #include "llresizebar.h" #include "llresizehandle.h" #include "llrootview.h" @@ -142,9 +143,22 @@ BOOL FSFloaterNearbyChat::postBuild() mChatLayoutPanelHeight = mChatLayoutPanel->getRect().getHeight(); mInputEditorPad = mChatLayoutPanelHeight - mInputEditor->getRect().getHeight(); - mEmojiButton = getChild("emoji_panel_btn"); - mEmojiButton->setLabel(LLUIString(LLWString(1, 128512))); - mEmojiButton->setClickedCallback(boost::bind(&FSFloaterNearbyChat::onEmojiPanelBtnClicked, this)); + mEmojiRecentPanelToggleBtn = getChild("emoji_recent_panel_toggle_btn"); + mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(this); }); + + mEmojiRecentPanel = getChild("emoji_recent_layout_panel"); + mEmojiRecentPanel->setVisible(false); + + mEmojiRecentEmptyText = getChildView("emoji_recent_empty_text"); + mEmojiRecentEmptyText->setVisible(false); + + mEmojiRecentIconsCtrl = getChild("emoji_recent_icons_ctrl"); + mEmojiRecentIconsCtrl->setCommitCallback([this](LLUICtrl*, const LLSD& value) { onRecentEmojiPicked(value); }); + mEmojiRecentIconsCtrl->setVisible(false); + + mEmojiPickerToggleBtn = getChild("emoji_picker_toggle_btn"); + mEmojiPickerToggleBtn->setLabel(LLUIString(LLWString(1, 128512))); + mEmojiPickerToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerToggleBtnClicked(this); }); getChild("chat_history_btn")->setCommitCallback(boost::bind(&FSFloaterNearbyChat::onHistoryButtonClicked, this)); getChild("chat_search_btn")->setCommitCallback(boost::bind(&FSFloaterNearbyChat::onSearchButtonClicked, this)); @@ -766,6 +780,8 @@ void FSFloaterNearbyChat::sendChat( EChatType type ) LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if (!text.empty()) { + FSCommon::updateUsedEmojis(text); + if(type == CHAT_TYPE_OOC) { std::string tempText = wstring_to_utf8str( text ); @@ -947,7 +963,54 @@ void FSFloaterNearbyChat::handleMinimized(bool minimized) } } -void FSFloaterNearbyChat::onEmojiPanelBtnClicked(FSFloaterNearbyChat* self) +void FSFloaterNearbyChat::onEmojiRecentPanelToggleBtnClicked(FSFloaterNearbyChat* self) +{ + BOOL show = !self->mEmojiRecentPanel->getVisible(); + if (show) + { + self->onEmojiRecentPanelOpening(); + } + self->mEmojiRecentPanel->setVisible(show); + self->mEmojiRecentPanelToggleBtn->setImageOverlay(show ? "Arrow_Up" : "Arrow_Down"); +} + +void FSFloaterNearbyChat::onEmojiRecentPanelOpening() +{ + std::list& recentlyUsed = LLFloaterEmojiPicker::getRecentlyUsed(); + if (recentlyUsed.empty()) + { + mEmojiRecentEmptyText->setVisible(true); + mEmojiRecentIconsCtrl->setVisible(false); + } + else + { + LLWString emojis; + for (llwchar emoji : recentlyUsed) + { + emojis += emoji; + } + mEmojiRecentIconsCtrl->setEmojis(emojis); + mEmojiRecentEmptyText->setVisible(false); + mEmojiRecentIconsCtrl->setVisible(true); + mEmojiRecentIconsCtrl->setFocus(true); + } +} + +void FSFloaterNearbyChat::onRecentEmojiPicked(const LLSD& value) +{ + LLSD::String str = value.asString(); + if (str.size()) + { + LLWString wstr = utf8string_to_wstring(str); + if (wstr.size()) + { + llwchar emoji = wstr[0]; + onEmojiPicked(emoji); + } + } +} + +void FSFloaterNearbyChat::onEmojiPickerToggleBtnClicked(FSFloaterNearbyChat* self) { if (LLFloaterEmojiPicker* picker = LLFloaterEmojiPicker::getInstance()) { diff --git a/indra/newview/fsfloaternearbychat.h b/indra/newview/fsfloaternearbychat.h index 43fb47c460..2654887494 100644 --- a/indra/newview/fsfloaternearbychat.h +++ b/indra/newview/fsfloaternearbychat.h @@ -39,6 +39,7 @@ class LLChatEntry; class LLComboBox; class LLLayoutStack; class LLLayoutPanel; +class LLPanelEmojiComplete; class LLResizeBar; class LLTextBox; @@ -120,7 +121,10 @@ private: bool onChatOptionsVisibleContextMenuItem(const LLSD& userdata); bool onChatOptionsEnableContextMenuItem(const LLSD& userdata); - static void onEmojiPanelBtnClicked(FSFloaterNearbyChat* self); + static void onEmojiRecentPanelToggleBtnClicked(FSFloaterNearbyChat* self); + static void onEmojiPickerToggleBtnClicked(FSFloaterNearbyChat* self); + void onEmojiRecentPanelOpening(); + void onRecentEmojiPicked(const LLSD& value); void onEmojiPicked(llwchar emoji); void onEmojiPickerClosed(); @@ -129,7 +133,11 @@ private: LLChatEntry* mInputEditor; // chat type selector and send chat buttons - LLButton* mEmojiButton; + LLButton* mEmojiRecentPanelToggleBtn; + LLButton* mEmojiPickerToggleBtn; + LLLayoutPanel* mEmojiRecentPanel; + LLView* mEmojiRecentEmptyText; + LLPanelEmojiComplete* mEmojiRecentIconsCtrl; LLButton* mSendChatButton; LLComboBox* mChatTypeCombo; diff --git a/indra/newview/fsnearbychathub.cpp b/indra/newview/fsnearbychathub.cpp index e607f503d5..1dd862d50c 100644 --- a/indra/newview/fsnearbychathub.cpp +++ b/indra/newview/fsnearbychathub.cpp @@ -253,6 +253,8 @@ void FSNearbyChat::sendChat(LLWString text, EChatType type) if (!text.empty()) { + FSCommon::updateUsedEmojis(text); + if (type == CHAT_TYPE_OOC) { text = utf8string_to_wstring(gSavedSettings.getString("FSOOCPrefix") + " ") + text + utf8string_to_wstring(" " + gSavedSettings.getString("FSOOCPostfix")); diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp index c3344fc18a..d24f627cdf 100644 --- a/indra/newview/llfloateremojipicker.cpp +++ b/indra/newview/llfloateremojipicker.cpp @@ -31,9 +31,9 @@ #include "llbutton.h" #include "llcombobox.h" #include "llemojidictionary.h" +#include "llfiltereditor.h" #include "llfloaterreg.h" #include "llkeyboard.h" -#include "lllineeditor.h" #include "llscrollcontainer.h" #include "llscrollingpanellist.h" #include "llscrolllistctrl.h" @@ -294,9 +294,9 @@ BOOL LLFloaterEmojiPicker::postBuild() mGroups = getChild("Groups"); mBadge = getChild("Badge"); - mFilter = getChild("Filter"); - mFilter->setKeystrokeCallback([this](LLLineEditor*, void*) { onSearchKeystroke(); }, NULL); - mFilter->setFont(LLViewerChat::getChatFont()); + mFilter = getChild("Filter"); + mFilter->setKeystrokeCallback([this](LLUICtrl*, const LLSD&) { onFilterChanged(); }); + mFilter->setTextChangedCallback([this](LLUICtrl*, const LLSD&) { onFilterChanged(); }); mFilter->setText(sFilterPattern); mEmojiScroll = getChild("EmojiGridContainer"); @@ -588,7 +588,7 @@ void LLFloaterEmojiPicker::onGroupButtonClick(LLUICtrl* ctrl) } } -void LLFloaterEmojiPicker::onSearchKeystroke() +void LLFloaterEmojiPicker::onFilterChanged() { sFilterPattern = mFilter->getText(); fillEmojis(); @@ -669,7 +669,6 @@ void LLFloaterEmojiPicker::onEmojiMouseUp(LLUICtrl* ctrl) { if (LLEmojiGridIcon* icon = dynamic_cast(ctrl)) { - onEmojiUsed(icon->getEmoji()); if (mEmojiPickCallback) { mEmojiPickCallback(icon->getEmoji()); @@ -723,6 +722,14 @@ void LLFloaterEmojiPicker::closeFloater(bool app_quitting) } } +// static +std::list& LLFloaterEmojiPicker::getRecentlyUsed() +{ + loadState(); + return sRecentlyUsed; +} + +// static void LLFloaterEmojiPicker::onEmojiUsed(llwchar emoji) { // Update sRecentlyUsed @@ -762,6 +769,7 @@ void LLFloaterEmojiPicker::onEmojiUsed(llwchar emoji) sFrequentlyUsed.push_back(std::make_pair(emoji, 1)); } +// static void LLFloaterEmojiPicker::loadState() { if (!sStateFileName.empty()) @@ -844,6 +852,7 @@ void LLFloaterEmojiPicker::loadState() } } +// static void LLFloaterEmojiPicker::saveState() { if (sStateFileName.empty()) diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h index e4b1216ce6..decee41721 100644 --- a/indra/newview/llfloateremojipicker.h +++ b/indra/newview/llfloateremojipicker.h @@ -54,13 +54,18 @@ public: virtual void closeFloater(bool app_quitting = false) override; + static std::list& getRecentlyUsed(); + static void onEmojiUsed(llwchar emoji); + static void loadState(); + static void saveState(); + private: void fillGroups(); void moveGroups(); void fillEmojis(bool fromResize = false); void onGroupButtonClick(LLUICtrl* ctrl); - void onSearchKeystroke(); + void onFilterChanged(); void onGridMouseEnter(); void onGridMouseLeave(); void onGroupButtonMouseEnter(LLUICtrl* ctrl); @@ -75,13 +80,9 @@ private: virtual BOOL handleKeyHere(KEY key, MASK mask) override; - void onEmojiUsed(llwchar emoji); - void loadState(); - void saveState(); - class LLPanel* mGroups { nullptr }; class LLPanel* mBadge { nullptr }; - class LLLineEditor* mFilter { nullptr }; + class LLFilterEditor* mFilter { nullptr }; class LLScrollContainer* mEmojiScroll { nullptr }; class LLScrollingPanelList* mEmojiGrid { nullptr }; class LLEmojiPreviewPanel* mPreview { nullptr }; diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 1c81e0aaaf..8e5715f128 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -601,6 +601,8 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type ) S32 channel = 0; stripChannelNumber(text, &channel); + updateUsedEmojis(text); + std::string utf8text = wstring_to_utf8str(text); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index a7698134f6..f0954aa955 100644 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -254,6 +254,8 @@ void LLFloaterIMSession::sendMsgFromInputEditor() LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if(!text.empty()) { + updateUsedEmojis(text); + // Truncate and convert to UTF8 for transport std::string utf8_text = wstring_to_utf8str(text); diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 101ee97edb..89ba3e2bf9 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -35,19 +35,21 @@ #include "llagentcamera.h" #include "llavataractions.h" #include "llavatariconctrl.h" -#include "llgroupiconctrl.h" #include "llchatentry.h" #include "llchathistory.h" #include "llchiclet.h" #include "llchicletbar.h" #include "lldraghandle.h" +#include "llemojidictionary.h" #include "llfloaterreg.h" #include "llfloateremojipicker.h" #include "llfloaterimsession.h" #include "llfloaterimcontainer.h" // to replace separate IM Floaters with multifloater container -#include "lllayoutstack.h" -#include "lltoolbarview.h" #include "llfloaterimnearbychat.h" +#include "llgroupiconctrl.h" +#include "lllayoutstack.h" +#include "llpanelemojicomplete.h" +#include "lltoolbarview.h" const F32 REFRESH_INTERVAL = 1.0f; const std::string ICN_GROUP("group_chat_icon"); @@ -59,7 +61,7 @@ void cb_group_do_nothing() } LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id) -: LLTransientDockableFloater(NULL, false, session_id), +: super(NULL, false, session_id), mIsP2PChat(false), mExpandCollapseBtn(NULL), mTearOffBtn(NULL), @@ -78,7 +80,7 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id) mInputPanels(NULL), mChatLayoutPanelHeight(0) { - setAutoFocus(FALSE); + setAutoFocus(FALSE); mSession = LLIMModel::getInstance()->findIMSession(mSessionID); mCommitCallbackRegistrar.add("IMSession.Menu.Action", @@ -91,12 +93,12 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id) boost::bind(&LLFloaterIMSessionTab::onIMShowModesMenuItemEnable, this, _2)); // Right click menu handling - mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLFloaterIMSessionTab::checkContextMenuItem, this, _2)); - mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMSessionTab::enableContextMenuItem, this, _2)); - mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMSessionTab::doToSelected, this, _2)); - mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&cb_group_do_nothing)); + mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLFloaterIMSessionTab::checkContextMenuItem, this, _2)); + mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMSessionTab::enableContextMenuItem, this, _2)); + mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMSessionTab::doToSelected, this, _2)); + mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&cb_group_do_nothing)); - mMinFloaterHeight = getMinHeight(); + mMinFloaterHeight = getMinHeight(); } LLFloaterIMSessionTab::~LLFloaterIMSessionTab() @@ -104,7 +106,7 @@ LLFloaterIMSessionTab::~LLFloaterIMSessionTab() delete mRefreshTimer; } -//static +// static LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uuid) { LLFloaterIMSessionTab* conv; @@ -121,7 +123,7 @@ LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uui return conv; }; -//static +// static LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid) { LLFloaterIMSessionTab* conv; @@ -137,14 +139,16 @@ LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid } return conv; + }; +// virtual void LLFloaterIMSessionTab::setVisible(BOOL visible) { - if(visible && !mHasVisibleBeenInitialized) + if (visible && !mHasVisibleBeenInitialized) { mHasVisibleBeenInitialized = true; - if(!gAgentCamera.cameraMouselook()) + if (!gAgentCamera.cameraMouselook()) { LLFloaterReg::getTypedInstance("im_container")->setVisible(true); } @@ -158,27 +162,26 @@ void LLFloaterIMSessionTab::setVisible(BOOL visible) mInputButtonPanel->setVisible(isTornOff()); } - LLTransientDockableFloater::setVisible(visible); + super::setVisible(visible); } -/*virtual*/ +// virtual void LLFloaterIMSessionTab::setFocus(BOOL focus) { - LLTransientDockableFloater::setFocus(focus); + super::setFocus(focus); - //Redirect focus to input editor - if (focus) + // Redirect focus to input editor + if (focus) { - updateMessages(); + updateMessages(); - if (mInputEditor) - { - mInputEditor->setFocus(TRUE); - } + if (mInputEditor) + { + mInputEditor->setFocus(TRUE); + } } } - void LLFloaterIMSessionTab::addToHost(const LLUUID& session_id) { if ((session_id.notNull() && !gIMMgr->hasSession(session_id)) @@ -223,44 +226,58 @@ void LLFloaterIMSessionTab::assignResizeLimits() { bool is_participants_pane_collapsed = mParticipantListPanel->isCollapsed(); - // disable a layoutstack's functionality when participant list panel is collapsed + // disable a layoutstack's functionality when participant list panel is collapsed mRightPartPanel->setIgnoreReshape(is_participants_pane_collapsed); - S32 participants_pane_target_width = is_participants_pane_collapsed? - 0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing()); + S32 participants_pane_target_width = is_participants_pane_collapsed? + 0 : (mParticipantListPanel->getRect().getWidth() + mParticipantListAndHistoryStack->getPanelSpacing()); - S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth; + S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth; setResizeLimits(new_min_width, getMinHeight()); this->mParticipantListAndHistoryStack->updateLayout(); } +// virtual BOOL LLFloaterIMSessionTab::postBuild() { BOOL result; mBodyStack = getChild("main_stack"); - mParticipantListAndHistoryStack = getChild("im_panels"); + mParticipantListAndHistoryStack = getChild("im_panels"); mCloseBtn = getChild("close_btn"); - mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); + mCloseBtn->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickClose(this); }); mExpandCollapseBtn = getChild("expand_collapse_btn"); - mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onSlide, this)); + mExpandCollapseBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onSlide(this); }); mExpandCollapseLineBtn = getChild("minz_btn"); - mExpandCollapseLineBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onCollapseToLine, this)); + mExpandCollapseLineBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onCollapseToLine(this); }); mTearOffBtn = getChild("tear_off_btn"); mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this)); - mEmojiBtn = getChild("emoji_panel_btn"); - mEmojiBtn->setLabel(LLUIString(LLWString(1, 128512))); - mEmojiBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onEmojiPanelBtnClicked, this)); + mEmojiRecentPanelToggleBtn = getChild("emoji_recent_panel_toggle_btn"); + mEmojiRecentPanelToggleBtn->setLabel(LLUIString(LLWString(1, 128512))); + mEmojiRecentPanelToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiRecentPanelToggleBtnClicked(this); }); + + mEmojiRecentPanel = getChild("emoji_recent_layout_panel"); + mEmojiRecentPanel->setVisible(false); + + mEmojiRecentEmptyText = getChildView("emoji_recent_empty_text"); + mEmojiRecentEmptyText->setVisible(false); + + mEmojiRecentIconsCtrl = getChild("emoji_recent_icons_ctrl"); + mEmojiRecentIconsCtrl->setCommitCallback([this](LLUICtrl*, const LLSD& value) { onRecentEmojiPicked(value); }); + mEmojiRecentIconsCtrl->setVisible(false); + + mEmojiPickerToggleBtn = getChild("emoji_picker_toggle_btn"); + mEmojiPickerToggleBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerToggleBtnClicked(this); }); mGearBtn = getChild("gear_btn"); - mAddBtn = getChild("add_btn"); + mAddBtn = getChild("add_btn"); mVoiceButton = getChild("voice_call_btn"); mParticipantListPanel = getChild("speakers_list_panel"); @@ -314,17 +331,17 @@ BOOL LLFloaterIMSessionTab::postBuild() // Create the root using an ad-hoc base item LLConversationItem* base_item = new LLConversationItem(mSessionID, mConversationViewModel); - LLFolderView::Params p(LLUICtrlFactory::getDefaultParams()); - p.rect = LLRect(0, 0, getRect().getWidth(), 0); - p.parent_panel = mParticipantListPanel; - p.listener = base_item; - p.view_model = &mConversationViewModel; - p.root = NULL; - p.use_ellipses = true; - p.options_menu = "menu_conversation.xml"; - p.name = "root"; + LLFolderView::Params p(LLUICtrlFactory::getDefaultParams()); + p.rect = LLRect(0, 0, getRect().getWidth(), 0); + p.parent_panel = mParticipantListPanel; + p.listener = base_item; + p.view_model = &mConversationViewModel; + p.root = NULL; + p.use_ellipses = true; + p.options_menu = "menu_conversation.xml"; + p.name = "root"; mConversationsRoot = LLUICtrlFactory::create(p); - mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); + mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); mConversationsRoot->setEnableRegistrar(&mEnableCallbackRegistrar); // Attach that root to the scroller mScroller->addChild(mConversationsRoot); @@ -364,6 +381,7 @@ LLParticipantList* LLFloaterIMSessionTab::getParticipantList() return dynamic_cast(LLFloaterIMContainer::getInstance()->getSessionModel(mSessionID)); } +// virtual void LLFloaterIMSessionTab::draw() { if (mRefreshTimer->hasExpired()) @@ -388,23 +406,24 @@ void LLFloaterIMSessionTab::draw() mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL); } - LLTransientDockableFloater::draw(); + super::draw(); } void LLFloaterIMSessionTab::enableDisableCallBtn() { - if (LLVoiceClient::instanceExists() && mVoiceButton) - { - mVoiceButton->setEnabled( - mSessionID.notNull() - && mSession - && mSession->mSessionInitialized - && LLVoiceClient::getInstance()->voiceEnabled() - && LLVoiceClient::getInstance()->isVoiceWorking() - && mSession->mCallBackEnabled); - } + if (LLVoiceClient::instanceExists() && mVoiceButton) + { + mVoiceButton->setEnabled( + mSessionID.notNull() + && mSession + && mSession->mSessionInitialized + && LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking() + && mSession->mCallBackEnabled); + } } +// virtual void LLFloaterIMSessionTab::onFocusReceived() { setBackgroundOpaque(true); @@ -414,13 +433,14 @@ void LLFloaterIMSessionTab::onFocusReceived() LLIMModel::instance().sendNoUnreadMessages(mSessionID); } - LLTransientDockableFloater::onFocusReceived(); + super::onFocusReceived(); } +// virtual void LLFloaterIMSessionTab::onFocusLost() { setBackgroundOpaque(false); - LLTransientDockableFloater::onFocusLost(); + super::onFocusLost(); } void LLFloaterIMSessionTab::onInputEditorClicked() @@ -433,7 +453,17 @@ void LLFloaterIMSessionTab::onInputEditorClicked() gToolBarView->flashCommand(LLCommandId("chat"), false); } -void LLFloaterIMSessionTab::onEmojiPanelBtnClicked(LLFloaterIMSessionTab* self) +void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked(LLFloaterIMSessionTab* self) +{ + BOOL show = !self->mEmojiRecentPanel->getVisible(); + if (show) + { + self->onEmojiRecentPanelOpening(); + } + self->mEmojiRecentPanel->setVisible(show); +} + +void LLFloaterIMSessionTab::onEmojiPickerToggleBtnClicked(LLFloaterIMSessionTab* self) { if (LLFloaterEmojiPicker* picker = LLFloaterEmojiPicker::getInstance()) { @@ -454,6 +484,43 @@ void LLFloaterIMSessionTab::onEmojiPanelBtnClicked(LLFloaterIMSessionTab* self) } } +void LLFloaterIMSessionTab::onEmojiRecentPanelOpening() +{ + std::list& recentlyUsed = LLFloaterEmojiPicker::getRecentlyUsed(); + if (recentlyUsed.empty()) + { + mEmojiRecentEmptyText->setVisible(true); + mEmojiRecentIconsCtrl->setVisible(false); + mEmojiPickerToggleBtn->setFocus(true); + } + else + { + LLWString emojis; + for (llwchar emoji : recentlyUsed) + { + emojis += emoji; + } + mEmojiRecentIconsCtrl->setEmojis(emojis); + mEmojiRecentEmptyText->setVisible(false); + mEmojiRecentIconsCtrl->setVisible(true); + mEmojiRecentIconsCtrl->setFocus(true); + } +} + +void LLFloaterIMSessionTab::onRecentEmojiPicked(const LLSD& value) +{ + LLSD::String str = value.asString(); + if (str.size()) + { + LLWString wstr = utf8string_to_wstring(str); + if (wstr.size()) + { + llwchar emoji = wstr[0]; + onEmojiPicked(emoji); + } + } +} + void LLFloaterIMSessionTab::onEmojiPicked(llwchar emoji) { mInputEditor->insertEmoji(emoji); @@ -464,6 +531,12 @@ void LLFloaterIMSessionTab::onEmojiPickerClosed() mInputEditor->setFocus(TRUE); } +void LLFloaterIMSessionTab::closeFloater(bool app_quitting) +{ + LLFloaterEmojiPicker::saveState(); + super::closeFloater(app_quitting); +} + std::string LLFloaterIMSessionTab::appendTime() { std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:" @@ -505,6 +578,20 @@ void LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD& args) mChatHistory->appendMessage(chat, chat_args); } +void LLFloaterIMSessionTab::updateUsedEmojis(LLWString text) +{ + LLEmojiDictionary* dictionary = LLEmojiDictionary::getInstance(); + llassert_always(dictionary); + + for (llwchar& c : text) + { + if (dictionary->isEmoji(c)) + { + LLFloaterEmojiPicker::onEmojiUsed(c); + } + } +} + static LLTrace::BlockTimerStatHandle FTM_BUILD_CONVERSATION_VIEW_PARTICIPANT("Build Conversation View"); void LLFloaterIMSessionTab::buildConversationViewParticipant() { @@ -532,10 +619,10 @@ void LLFloaterIMSessionTab::buildConversationViewParticipant() while (current_participant_model != end_participant_model) { LLConversationItem* participant_model = dynamic_cast(*current_participant_model); - if (participant_model) - { - addConversationViewParticipant(participant_model); - } + if (participant_model) + { + addConversationViewParticipant(participant_model); + } current_participant_model++; } } @@ -555,10 +642,10 @@ void LLFloaterIMSessionTab::addConversationViewParticipant(LLConversationItem* p // If not already present, create the participant view and attach it to the root, otherwise, just refresh it if (widget) { - if (update_view) - { - updateConversationViewParticipant(uuid); // overkill? - } + if (update_view) + { + updateConversationViewParticipant(uuid); // overkill? + } } else { @@ -608,11 +695,11 @@ void LLFloaterIMSessionTab::refreshConversation() { participants_uuids.push_back(widget_it->first); } - if (widget_it->second->getViewModelItem()) - { - widget_it->second->refresh(); - widget_it->second->setVisible(TRUE); - } + if (widget_it->second->getViewModelItem()) + { + widget_it->second->refresh(); + widget_it->second->setVisible(TRUE); + } ++widget_it; } if (is_ad_hoc || mIsP2PChat) @@ -668,7 +755,7 @@ void LLFloaterIMSessionTab::refreshConversation() // Copied from LLFloaterIMContainer::createConversationViewParticipant(). Refactor opportunity! LLConversationViewParticipant* LLFloaterIMSessionTab::createConversationViewParticipant(LLConversationItem* item) { - LLRect panel_rect = mParticipantListPanel->getRect(); + LLRect panel_rect = mParticipantListPanel->getRect(); LLConversationViewParticipant::Params params; params.name = item->getDisplayName(); @@ -796,7 +883,7 @@ void LLFloaterIMSessionTab::hideAllStandardButtons() void LLFloaterIMSessionTab::updateHeaderAndToolbar() { // prevent start conversation before its container - LLFloaterIMContainer::getInstance(); + LLFloaterIMContainer::getInstance(); bool is_not_torn_off = !checkIfTornOff(); if (is_not_torn_off) @@ -813,12 +900,12 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar() && !mIsP2PChat; mParticipantListAndHistoryStack->collapsePanel(mParticipantListPanel, !is_participant_list_visible); - mParticipantListPanel->setVisible(is_participant_list_visible); + mParticipantListPanel->setVisible(is_participant_list_visible); // Display collapse image (<<) if the floater is hosted // or if it is torn off but has an open control panel. bool is_expanded = is_not_torn_off || is_participant_list_visible; - + mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); mExpandCollapseBtn->setToolTip( is_not_torn_off? @@ -847,10 +934,10 @@ void LLFloaterIMSessionTab::updateHeaderAndToolbar() void LLFloaterIMSessionTab::forceReshape() { - LLRect floater_rect = getRect(); - reshape(llmax(floater_rect.getWidth(), this->getMinWidth()), - llmax(floater_rect.getHeight(), this->getMinHeight()), - true); + LLRect floater_rect = getRect(); + reshape(llmax(floater_rect.getWidth(), this->getMinWidth()), + llmax(floater_rect.getHeight(), this->getMinHeight()), + true); } @@ -876,7 +963,7 @@ void LLFloaterIMSessionTab::processChatHistoryStyleUpdate(bool clean_messages/* LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); if (nearby_chat) { - nearby_chat->reloadMessages(clean_messages); + nearby_chat->reloadMessages(clean_messages); } } @@ -922,15 +1009,15 @@ void LLFloaterIMSessionTab::onSlide(LLFloaterIMSessionTab* self) { if (!self->mIsP2PChat) { - // The state must toggle the collapsed state of the panel - should_be_expanded = self->mParticipantListPanel->isCollapsed(); + // The state must toggle the collapsed state of the panel + should_be_expanded = self->mParticipantListPanel->isCollapsed(); // Update the expand/collapse flag of the participant list panel and save it - gSavedSettings.setBOOL("IMShowControlPanel", should_be_expanded); - self->mIsParticipantListExpanded = should_be_expanded; - - // Refresh for immediate feedback - self->refreshConversation(); + gSavedSettings.setBOOL("IMShowControlPanel", should_be_expanded); + self->mIsParticipantListExpanded = should_be_expanded; + + // Refresh for immediate feedback + self->refreshConversation(); } } @@ -967,12 +1054,12 @@ void LLFloaterIMSessionTab::reshapeFloater(bool collapse) + mChatLayoutPanel->getRect().getHeight() - mChatLayoutPanelHeight + 2; floater_rect.mTop -= height; - setResizeLimits(getMinWidth(), floater_rect.getHeight()); + setResizeLimits(getMinWidth(), floater_rect.getHeight()); } else { floater_rect.mTop = floater_rect.mBottom + mFloaterHeight; - setResizeLimits(getMinWidth(), mMinFloaterHeight); + setResizeLimits(getMinWidth(), mMinFloaterHeight); } enableResizeCtrls(true, true, !collapse); @@ -997,7 +1084,7 @@ void LLFloaterIMSessionTab::restoreFloater() setShape(floater_rect, true); mBodyStack->updateLayout(); mExpandCollapseLineBtn->setImageOverlay(getString("expandline_icon")); - setResizeLimits(getMinWidth(), mMinFloaterHeight); + setResizeLimits(getMinWidth(), mMinFloaterHeight); setMessagePaneExpanded(true); saveCollapsedState(); mInputEditor->enableSingleLineMode(false); @@ -1023,8 +1110,8 @@ void LLFloaterIMSessionTab::onTearOffClicked() { restoreFloater(); setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE); - mSaveRect = isTornOff(); - initRectControl(); + mSaveRect = isTornOff(); + initRectControl(); LLFloater::onClickTearOff(this); LLFloaterIMContainer* container = LLFloaterReg::findTypedInstance("im_container"); @@ -1114,8 +1201,8 @@ bool LLFloaterIMSessionTab::checkIfTornOff() void LLFloaterIMSessionTab::doToSelected(const LLSD& userdata) { // Get the list of selected items in the tab - std::string command = userdata.asString(); - uuid_vec_t selected_uuids; + std::string command = userdata.asString(); + uuid_vec_t selected_uuids; getSelectedUUIDs(selected_uuids); // Perform the command (IM, profile, etc...) on the list using the general conversation container method @@ -1127,8 +1214,8 @@ void LLFloaterIMSessionTab::doToSelected(const LLSD& userdata) bool LLFloaterIMSessionTab::enableContextMenuItem(const LLSD& userdata) { // Get the list of selected items in the tab - std::string command = userdata.asString(); - uuid_vec_t selected_uuids; + std::string command = userdata.asString(); + uuid_vec_t selected_uuids; getSelectedUUIDs(selected_uuids); // Perform the item enable test on the list using the general conversation container method @@ -1139,8 +1226,8 @@ bool LLFloaterIMSessionTab::enableContextMenuItem(const LLSD& userdata) bool LLFloaterIMSessionTab::checkContextMenuItem(const LLSD& userdata) { // Get the list of selected items in the tab - std::string command = userdata.asString(); - uuid_vec_t selected_uuids; + std::string command = userdata.asString(); + uuid_vec_t selected_uuids; getSelectedUUIDs(selected_uuids); // Perform the item check on the list using the general conversation container method @@ -1150,19 +1237,19 @@ bool LLFloaterIMSessionTab::checkContextMenuItem(const LLSD& userdata) void LLFloaterIMSessionTab::getSelectedUUIDs(uuid_vec_t& selected_uuids) { - const std::set selected_items = mConversationsRoot->getSelectionList(); + const std::set selected_items = mConversationsRoot->getSelectionList(); - std::set::const_iterator it = selected_items.begin(); - const std::set::const_iterator it_end = selected_items.end(); + std::set::const_iterator it = selected_items.begin(); + const std::set::const_iterator it_end = selected_items.end(); - for (; it != it_end; ++it) - { - LLConversationItem* conversation_item = static_cast((*it)->getViewModelItem()); - if (conversation_item) - { - selected_uuids.push_back(conversation_item->getUUID()); - } - } + for (; it != it_end; ++it) + { + LLConversationItem* conversation_item = static_cast((*it)->getViewModelItem()); + if (conversation_item) + { + selected_uuids.push_back(conversation_item->getUUID()); + } + } } LLConversationItem* LLFloaterIMSessionTab::getCurSelectedViewModelItem() @@ -1170,8 +1257,8 @@ LLConversationItem* LLFloaterIMSessionTab::getCurSelectedViewModelItem() LLConversationItem *conversationItem = NULL; if(mConversationsRoot && - mConversationsRoot->getCurSelectedItem() && - mConversationsRoot->getCurSelectedItem()->getViewModelItem()) + mConversationsRoot->getCurSelectedItem() && + mConversationsRoot->getCurSelectedItem()->getViewModelItem()) { conversationItem = static_cast(mConversationsRoot->getCurSelectedItem()->getViewModelItem()) ; } diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index 3539b2a0af..4a1024322a 100644 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -43,10 +43,12 @@ class LLPanelChatControlPanel; class LLChatEntry; class LLChatHistory; +class LLPanelEmojiComplete; class LLFloaterIMSessionTab : public LLTransientDockableFloater { + using super = LLTransientDockableFloater; public: LOG_CLASS(LLFloaterIMSessionTab); @@ -70,9 +72,8 @@ public: bool isHostAttached() {return mIsHostAttached;} void setHostAttached(bool is_attached) {mIsHostAttached = is_attached;} - static LLFloaterIMSessionTab* findConversation(const LLUUID& uuid); - static LLFloaterIMSessionTab* getConversation(const LLUUID& uuid); - + static LLFloaterIMSessionTab* findConversation(const LLUUID& uuid); + static LLFloaterIMSessionTab* getConversation(const LLUUID& uuid); bool isNearbyChat() {return mIsNearbyChat;} @@ -82,6 +83,7 @@ public: /*virtual*/ void draw(); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ void setFocus(BOOL focus); + /*virtual*/ void closeFloater(bool app_quitting = false); // Handle the left hand participant list widgets void addConversationViewParticipant(LLConversationItem* item, bool update_view = true); @@ -138,8 +140,8 @@ protected: virtual void enableDisableCallBtn(); // process focus events to set a currently active session - /* virtual */ void onFocusLost(); /* virtual */ void onFocusReceived(); + /* virtual */ void onFocusLost(); // prepare chat's params and out one message to chatHistory void appendMessage(const LLChat& chat, const LLSD& args = LLSD()); @@ -147,6 +149,8 @@ protected: std::string appendTime(); void assignResizeLimits(); + void updateUsedEmojis(LLWString text); + S32 mFloaterExtraWidth; bool mIsNearbyChat; @@ -154,8 +158,7 @@ protected: bool mMessagePaneExpanded; bool mIsParticipantListExpanded; - S32 mMinFloaterHeight; - + S32 mMinFloaterHeight; LLIMModel::LLIMSession* mSession; @@ -170,33 +173,37 @@ protected: LLLayoutPanel* mContentPanel; LLLayoutPanel* mToolbarPanel; LLLayoutPanel* mInputButtonPanel; + LLLayoutPanel* mEmojiRecentPanel; + LLView* mEmojiRecentEmptyText; + LLPanelEmojiComplete* mEmojiRecentIconsCtrl; LLParticipantList* getParticipantList(); conversations_widgets_map mConversationsWidgets; LLConversationViewModel mConversationViewModel; LLFolderView* mConversationsRoot; LLScrollContainer* mScroller; - LLChatHistory* mChatHistory; + LLChatHistory* mChatHistory; LLChatEntry* mInputEditor; - LLLayoutPanel * mChatLayoutPanel; - LLLayoutStack * mInputPanels; + LLLayoutPanel* mChatLayoutPanel; + LLLayoutStack* mInputPanels; LLButton* mExpandCollapseLineBtn; LLButton* mExpandCollapseBtn; LLButton* mTearOffBtn; - LLButton* mEmojiBtn; + LLButton* mEmojiRecentPanelToggleBtn; + LLButton* mEmojiPickerToggleBtn; LLButton* mCloseBtn; LLButton* mGearBtn; LLButton* mAddBtn; - LLButton* mVoiceButton; + LLButton* mVoiceButton; private: // Handling selection and contextual menu - void doToSelected(const LLSD& userdata); - bool enableContextMenuItem(const LLSD& userdata); - bool checkContextMenuItem(const LLSD& userdata); + void doToSelected(const LLSD& userdata); + bool enableContextMenuItem(const LLSD& userdata); + bool checkContextMenuItem(const LLSD& userdata); - void getSelectedUUIDs(uuid_vec_t& selected_uuids); + void getSelectedUUIDs(uuid_vec_t& selected_uuids); /// Refreshes the floater at a constant rate. virtual void refresh() = 0; @@ -210,13 +217,16 @@ private: void onInputEditorClicked(); - static void onEmojiPanelBtnClicked(LLFloaterIMSessionTab* self); + static void onEmojiRecentPanelToggleBtnClicked(LLFloaterIMSessionTab* self); + static void onEmojiPickerToggleBtnClicked(LLFloaterIMSessionTab* self); + void onEmojiRecentPanelOpening(); + void onRecentEmojiPicked(const LLSD& value); void onEmojiPicked(llwchar emoji); void onEmojiPickerClosed(); bool checkIfTornOff(); - bool mIsHostAttached; - bool mHasVisibleBeenInitialized; + bool mIsHostAttached; + bool mHasVisibleBeenInitialized; LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index ddc0e85808..741ca10bac 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -41,6 +41,7 @@ static LLDefaultChildRegistry::Register r("emoji_complete" LLPanelEmojiComplete::Params::Params() : autosize("autosize") + , noscroll("noscroll") , max_emoji("max_emoji") , padding("padding") , selected_image("selected_image") @@ -50,6 +51,7 @@ LLPanelEmojiComplete::Params::Params() LLPanelEmojiComplete::LLPanelEmojiComplete(const LLPanelEmojiComplete::Params& p) : LLUICtrl(p) , mAutoSize(p.autosize) + , mNoScroll(p.noscroll) , mMaxVisible(p.max_emoji) , mPadding(p.padding) , mSelectedImage(p.selected_image) @@ -152,7 +154,7 @@ BOOL LLPanelEmojiComplete::handleMouseUp(S32 x, S32 y, MASK mask) void LLPanelEmojiComplete::onCommit() { - if (npos != mCurSelected) + if (mCurSelected < mEmojis.size()) { LLWString wstr; wstr.push_back(mEmojis.at(mCurSelected)); @@ -167,6 +169,14 @@ void LLPanelEmojiComplete::reshape(S32 width, S32 height, BOOL called_from_paren updateConstraints(); } +void LLPanelEmojiComplete::setEmojis(const LLWString& emojis) +{ + mEmojis = emojis; + mCurSelected = 0; + + onEmojisChanged(); +} + void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) { llwchar curEmoji = (mCurSelected < mEmojis.size()) ? mEmojis.at(mCurSelected) : 0; @@ -175,6 +185,11 @@ void LLPanelEmojiComplete::setEmojiHint(const std::string& hint) size_t curEmojiIdx = (curEmoji) ? mEmojis.find(curEmoji) : std::string::npos; mCurSelected = (std::string::npos != curEmojiIdx) ? curEmojiIdx : 0; + onEmojisChanged(); +} + +void LLPanelEmojiComplete::onEmojisChanged() +{ if (mAutoSize) { mVisibleEmojis = (U16)std::min(mEmojis.size(), mMaxVisible); @@ -233,9 +248,13 @@ void LLPanelEmojiComplete::updateConstraints() void LLPanelEmojiComplete::updateScrollPos() { const size_t cntEmoji = mEmojis.size(); - if (0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected) + if (mNoScroll || 0 == cntEmoji || cntEmoji < mVisibleEmojis || 0 == mCurSelected) { mScrollPos = 0; + if (mCurSelected >= mVisibleEmojis) + { + mCurSelected = mVisibleEmojis ? mVisibleEmojis - 1 : 0; + } } else if (cntEmoji - 1 == mCurSelected) { @@ -273,11 +292,11 @@ BOOL LLFloaterEmojiComplete::handleKey(KEY key, MASK mask, BOOL called_from_pare handled = true; break; } - } if (handled) return TRUE; + return LLFloater::handleKey(key, mask, called_from_parent); } diff --git a/indra/newview/llpanelemojicomplete.h b/indra/newview/llpanelemojicomplete.h index aa0f806525..3456de4044 100644 --- a/indra/newview/llpanelemojicomplete.h +++ b/indra/newview/llpanelemojicomplete.h @@ -40,6 +40,7 @@ public: struct Params : public LLInitParam::Block { Optional autosize; + Optional noscroll; Optional max_emoji, padding; @@ -50,6 +51,7 @@ public: protected: LLPanelEmojiComplete(const LLPanelEmojiComplete::Params&); + public: virtual ~LLPanelEmojiComplete(); @@ -62,9 +64,13 @@ public: void reshape(S32 width, S32 height, BOOL called_from_parent) override; public: + const LLWString& getEmojis() const { return mEmojis; } size_t getEmojiCount() const { return mEmojis.size(); } - void setEmojiHint(const std::string& hint); + void setEmojis(const LLWString& emojis); + void setEmojiHint(const std::string& hint); + protected: + void LLPanelEmojiComplete::onEmojisChanged(); size_t posToIndex(S32 x, S32 y) const; void select(size_t emoji_idx); void selectNext(); @@ -76,13 +82,14 @@ protected: protected: static constexpr auto npos = std::numeric_limits::max(); - bool mAutoSize = false; + const bool mAutoSize = false; + const bool mNoScroll = false; const LLFontGL* mFont; U16 mEmojiWidth = 0; - size_t mMaxVisible = 0; - S32 mPadding = 8; + const size_t mMaxVisible = 0; + const S32 mPadding = 8; LLRect mRenderRect; - LLUIImagePtr mSelectedImage; + const LLUIImagePtr mSelectedImage; LLWString mEmojis; size_t mVisibleEmojis = 0; 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 acef2fb5e0..48a8bcbe68 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,13 +66,19 @@ -