diff options
author | Alexander Gavriliuk <alexandrgproductengine@lindenlab.com> | 2023-07-04 07:38:05 +0200 |
---|---|---|
committer | Guru <alexandrgproductengine@lindenlab.com> | 2023-07-05 10:07:19 +0200 |
commit | 8bbbce015b6dae1bdafe0bba329463322642ca85 (patch) | |
tree | 82b01abfaad33f9bdd3586d50bf045c91b84953b /indra | |
parent | 886c6089672cc24a4ce5d2f84f388087e7d6281b (diff) |
SL-19575 Rework emoji picker layout similar to Slack
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llui/llscrollingpanellist.cpp | 184 | ||||
-rw-r--r-- | indra/llui/llscrollingpanellist.h | 37 | ||||
-rw-r--r-- | indra/newview/llfloateremojipicker.cpp | 375 | ||||
-rw-r--r-- | indra/newview/llfloateremojipicker.h | 27 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_activeim.xml | 2 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_emoji_picker.xml | 29 |
6 files changed, 544 insertions, 110 deletions
diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp index b6f2eb8ba2..3a819e7d06 100644 --- a/indra/llui/llscrollingpanellist.cpp +++ b/indra/llui/llscrollingpanellist.cpp @@ -37,53 +37,44 @@ static LLDefaultChildRegistry::Register<LLScrollingPanelList> r("scrolling_panel // This could probably be integrated with LLScrollContainer -SJB +LLScrollingPanelList::Params::Params() + : is_horizontal("is_horizontal") + , padding("padding") + , spacing("spacing") +{ +} + +LLScrollingPanelList::LLScrollingPanelList(const Params& p) + : LLUICtrl(p) + , mIsHorizontal(p.is_horizontal) + , mPadding(p.padding.isProvided() ? p.padding : DEFAULT_PADDING) + , mSpacing(p.spacing.isProvided() ? p.spacing : DEFAULT_SPACING) +{ +} + void LLScrollingPanelList::clearPanels() { deleteAllChildren(); mPanelList.clear(); - - LLRect rc = getRect(); - rc.setLeftTopAndSize(rc.mLeft, rc.mTop, 1, 1); - setRect(rc); - - notifySizeChanged(rc.getHeight()); + rearrange(); } -S32 LLScrollingPanelList::addPanel( LLScrollingPanel* panel ) +S32 LLScrollingPanelList::addPanel(LLScrollingPanel* panel, bool back) { - addChildInBack( panel ); - mPanelList.push_front( panel ); - - // Resize this view - S32 total_height = 0; - S32 max_width = 0; - S32 cur_gap = 0; - for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); - iter != mPanelList.end(); ++iter) + if (back) { - LLScrollingPanel *childp = *iter; - total_height += childp->getRect().getHeight() + cur_gap; - max_width = llmax( max_width, childp->getRect().getWidth() ); - cur_gap = GAP_BETWEEN_PANELS; + addChild(panel); + mPanelList.push_back(panel); } - LLRect rc = getRect(); - rc.setLeftTopAndSize(rc.mLeft, rc.mTop, max_width, total_height); - setRect(rc); - - notifySizeChanged(rc.getHeight()); - - // Reposition each of the child views - S32 cur_y = total_height; - for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); - iter != mPanelList.end(); ++iter) + else { - LLScrollingPanel *childp = *iter; - cur_y -= childp->getRect().getHeight(); - childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom); - cur_y -= GAP_BETWEEN_PANELS; + addChildInBack(panel); + mPanelList.push_front(panel); } - return total_height; + rearrange(); + + return mIsHorizontal ? getRect().getWidth() : getRect().getHeight(); } void LLScrollingPanelList::removePanel(LLScrollingPanel* panel) @@ -100,7 +91,7 @@ void LLScrollingPanelList::removePanel(LLScrollingPanel* panel) break; } } - if(iter != mPanelList.end()) + if (iter != mPanelList.end()) { removePanel(index); } @@ -120,62 +111,104 @@ void LLScrollingPanelList::removePanel( U32 panel_index ) mPanelList.erase( mPanelList.begin() + panel_index ); } - const S32 GAP_BETWEEN_PANELS = 6; + rearrange(); +} - // Resize this view - S32 total_height = 0; - S32 max_width = 0; - S32 cur_gap = 0; - for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); +void LLScrollingPanelList::updatePanels(BOOL allow_modify) +{ + for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); iter != mPanelList.end(); ++iter) - { + { LLScrollingPanel *childp = *iter; - total_height += childp->getRect().getHeight() + cur_gap; - max_width = llmax( max_width, childp->getRect().getWidth() ); - cur_gap = GAP_BETWEEN_PANELS; + childp->updatePanel(allow_modify); + } +} + +void LLScrollingPanelList::rearrange() +{ + // Resize this view + S32 new_width, new_height; + if (!mPanelList.empty()) + { + new_width = new_height = mPadding * 2; + for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); + iter != mPanelList.end(); ++iter) + { + LLScrollingPanel* childp = *iter; + const LLRect& rect = childp->getRect(); + if (mIsHorizontal) + { + new_width += rect.getWidth() + mSpacing; + new_height = llmax(new_height, rect.getHeight()); + } + else + { + new_height += rect.getHeight() + mSpacing; + new_width = llmax(new_width, rect.getWidth()); + } + } + + if (mIsHorizontal) + { + new_width -= mSpacing; + } + else + { + new_height -= mSpacing; + } } + else + { + new_width = new_height = 1; + } + LLRect rc = getRect(); - rc.setLeftTopAndSize(rc.mLeft, rc.mTop, max_width, total_height); - setRect(rc); + if (mIsHorizontal || !followsRight()) + { + rc.mRight = rc.mLeft + new_width; + } + if (!mIsHorizontal || !followsBottom()) + { + rc.mBottom = rc.mTop - new_height; + } - notifySizeChanged(rc.getHeight()); + if (rc.mRight != getRect().mRight || rc.mBottom != getRect().mBottom) + { + setRect(rc); + notifySizeChanged(); + } // Reposition each of the child views - S32 cur_y = total_height; + S32 pos = mIsHorizontal ? mPadding : rc.getHeight() - mPadding; for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); - iter != mPanelList.end(); ++iter) + iter != mPanelList.end(); ++iter) { - LLScrollingPanel *childp = *iter; - cur_y -= childp->getRect().getHeight(); - childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom); - cur_y -= GAP_BETWEEN_PANELS; + LLScrollingPanel* childp = *iter; + const LLRect& rect = childp->getRect(); + if (mIsHorizontal) + { + childp->translate(pos - rect.mLeft, rc.getHeight() - mPadding - rect.mTop); + pos += rect.getWidth() + mSpacing; + } + else + { + childp->translate(mPadding - rect.mLeft, pos - rect.mTop); + pos -= rect.getHeight() + mSpacing; + } } } -void LLScrollingPanelList::updatePanels(BOOL allow_modify) -{ - for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); - iter != mPanelList.end(); ++iter) - { - LLScrollingPanel *childp = *iter; - childp->updatePanel(allow_modify); - } -} - void LLScrollingPanelList::updatePanelVisiblilty() { // Determine visibility of children. - S32 BORDER_WIDTH = 2; // HACK - LLRect parent_local_rect = getParent()->getRect(); - parent_local_rect.stretch( -BORDER_WIDTH ); - LLRect parent_screen_rect; - getParent()->localPointToScreen( - BORDER_WIDTH, 0, + getParent()->localPointToScreen( + mPadding, mPadding, &parent_screen_rect.mLeft, &parent_screen_rect.mBottom ); - getParent()->localPointToScreen( - parent_local_rect.getWidth() - BORDER_WIDTH, parent_local_rect.getHeight() - BORDER_WIDTH, + getParent()->localPointToScreen( + getParent()->getRect().getWidth() - mPadding, + getParent()->getRect().getHeight() - mPadding, &parent_screen_rect.mRight, &parent_screen_rect.mTop ); for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin(); @@ -207,11 +240,12 @@ void LLScrollingPanelList::draw() LLUICtrl::draw(); } -void LLScrollingPanelList::notifySizeChanged(S32 height) +void LLScrollingPanelList::notifySizeChanged() { LLSD info; info["action"] = "size_changes"; - info["height"] = height; + info["height"] = getRect().getHeight(); + info["width"] = getRect().getWidth(); notifyParent(info); } diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h index e8df176ec3..9dc65dabb5 100644 --- a/indra/llui/llscrollingpanellist.h +++ b/indra/llui/llscrollingpanellist.h @@ -51,12 +51,18 @@ class LLScrollingPanelList : public LLUICtrl { public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> - {}; - LLScrollingPanelList(const Params& p) - : LLUICtrl(p) - {} + { + Optional<bool> is_horizontal; + Optional<S32> padding; + Optional<S32> spacing; + + Params(); + }; + + LLScrollingPanelList(const Params& p); - static const S32 GAP_BETWEEN_PANELS = 6; + static const S32 DEFAULT_SPACING = 6; + static const S32 DEFAULT_PADDING = 2; typedef std::deque<LLScrollingPanel*> panel_list_t; @@ -65,11 +71,18 @@ public: virtual void draw(); void clearPanels(); - S32 addPanel( LLScrollingPanel* panel ); - void removePanel( LLScrollingPanel* panel ); - void removePanel( U32 panel_index ); + S32 addPanel(LLScrollingPanel* panel, bool back = false); + void removePanel(LLScrollingPanel* panel); + void removePanel(U32 panel_index); void updatePanels(BOOL allow_modify); - const panel_list_t& getPanelList() { return mPanelList; } + void rearrange(); + + const panel_list_t& getPanelList() const { return mPanelList; } + bool getIsHorizontal() const { return mIsHorizontal; } + void setPadding(S32 padding) { mPadding = padding; rearrange(); } + void setSpacing(S32 spacing) { mSpacing = spacing; rearrange(); } + S32 getPadding() const { return mPadding; } + S32 getSpacing() const { return mSpacing; } private: void updatePanelVisiblilty(); @@ -77,7 +90,11 @@ private: /** * Notify parent about size change, makes sense when used inside accordion */ - void notifySizeChanged(S32 height); + void notifySizeChanged(); + + bool mIsHorizontal; + S32 mPadding; + S32 mSpacing; panel_list_t mPanelList; }; diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp index 7fbaaaaa89..8c7f38d324 100644 --- a/indra/newview/llfloateremojipicker.cpp +++ b/indra/newview/llfloateremojipicker.cpp @@ -30,7 +30,10 @@ #include "llcombobox.h" #include "llemojidictionary.h" #include "llfloaterreg.h" +#include "llkeyboard.h" #include "lllineeditor.h" +#include "llscrollcontainer.h" +#include "llscrollingpanellist.h" #include "llscrolllistctrl.h" #include "llscrolllistitem.h" #include "lltextbox.h" @@ -39,6 +42,7 @@ std::string LLFloaterEmojiPicker::mSelectedCategory; std::string LLFloaterEmojiPicker::mSearchPattern; int LLFloaterEmojiPicker::mSelectedEmojiIndex; +bool LLFloaterEmojiPicker::mUseGrid = true; class LLEmojiScrollListItem : public LLScrollListItem { @@ -85,6 +89,114 @@ private: llwchar mEmoji; }; +class LLEmojiGridRow : public LLScrollingPanel +{ +public: + LLEmojiGridRow(const LLPanel::Params& panel_params, + const LLScrollingPanelList::Params& list_params) + : LLScrollingPanel(panel_params) + , mList(new LLScrollingPanelList(list_params)) + { + addChild(mList); + } + + virtual void updatePanel(BOOL allow_modify) + { + } + +public: + LLScrollingPanelList* mList; +}; + +class LLEmojiGridDivider : public LLScrollingPanel +{ +public: + LLEmojiGridDivider(const LLPanel::Params& panel_params, std::string text) + : LLScrollingPanel(panel_params) + , mText(utf8string_to_wstring(text)) + { + } + + virtual void draw() override + { + LLScrollingPanel::draw(); + + F32 x = 4; // padding-left + F32 y = getRect().getHeight() / 2; + LLFontGL::getFontSansSerifBold()->render( + mText, // wstr + 0, // begin_offset + x, // x + y, // y + LLColor4::white, // color + LLFontGL::LEFT, // halign + LLFontGL::VCENTER, // valign + LLFontGL::NORMAL, // style + LLFontGL::DROP_SHADOW_SOFT, // shadow + mText.size(), // max_chars + S32_MAX, // max_pixels + nullptr, // right_x + false, // use_ellipses + true); // use_color + } + + virtual void updatePanel(BOOL allow_modify) + { + } + +private: + const LLWString mText; +}; + +class LLEmojiGridIcon : public LLScrollingPanel +{ +public: + LLEmojiGridIcon(const LLPanel::Params& panel_params, const LLEmojiDescriptor* descr, std::string category) + : LLScrollingPanel(panel_params) + , mEmoji(descr->Character) + , mText(LLWString(1, mEmoji)) + , mDescr(descr->Name) + , mCategory(category) + { + } + + virtual void draw() override + { + LLScrollingPanel::draw(); + + F32 x = getRect().getWidth() / 2; + F32 y = getRect().getHeight() / 2; + LLFontGL::getFontEmoji()->render( + mText, // wstr + 0, // begin_offset + x, // x + y, // y + LLColor4::white, // color + LLFontGL::HCENTER, // halign + LLFontGL::VCENTER, // valign + LLFontGL::NORMAL, // style + LLFontGL::DROP_SHADOW_SOFT, // shadow + 1, // max_chars + S32_MAX, // max_pixels + nullptr, // right_x + false, // use_ellipses + true); // use_color + } + + virtual void updatePanel(BOOL allow_modify) {} + + llwchar getEmoji() const { return mEmoji; } + LLWString getText() const { return mText; } + std::string getDescr() const { return mDescr; } + std::string getCategory() const { return mCategory; } + +private: + const llwchar mEmoji; + const LLWString mText; + const std::string mDescr; + const std::string mCategory; +}; + LLFloaterEmojiPicker* LLFloaterEmojiPicker::getInstance() { LLFloaterEmojiPicker* floater = LLFloaterReg::getTypedInstance<LLFloaterEmojiPicker>("emoji_picker"); @@ -102,6 +214,9 @@ LLFloaterEmojiPicker* LLFloaterEmojiPicker::showInstance(pick_callback_t pick_ca void LLFloaterEmojiPicker::show(pick_callback_t pick_callback, close_callback_t close_callback) { + // Temporary solution to support both layouts + mUseGrid = !gKeyboard->getKeyDown(KEY_SHIFT); + mEmojiPickCallback = pick_callback; mFloaterCloseCallback = close_callback; openFloater(mKey); @@ -119,6 +234,9 @@ BOOL LLFloaterEmojiPicker::postBuild() mPreviewEmoji = getChild<LLButton>("PreviewEmoji"); mPreviewEmoji->setClickedCallback([this](LLUICtrl*, const LLSD&) { onPreviewEmojiClick(); }); + mDescription = getChild<LLTextBox>("Description"); + mDescription->setVisible(FALSE); + mCategory = getChild<LLComboBox>("Category"); mCategory->setCommitCallback([this](LLUICtrl*, const LLSD&) { onCategoryCommit(); }); const LLEmojiDictionary::cat2descrs_map_t& cat2Descrs = LLEmojiDictionary::instance().getCategory2Descrs(); @@ -137,14 +255,33 @@ BOOL LLFloaterEmojiPicker::postBuild() mSearch->setFont(LLViewerChat::getChatFont()); mSearch->setText(mSearchPattern); - mEmojis = getChild<LLScrollListCtrl>("Emojis"); - mEmojis->setCommitCallback([this](LLUICtrl*, const LLSD&) { onEmojiSelect(); }); - mEmojis->setDoubleClickCallback([this]() { onEmojiPick(); }); + mEmojiList = getChild<LLScrollListCtrl>("EmojiList"); + mEmojiList->setCommitCallback([this](LLUICtrl*, const LLSD&) { onEmojiSelect(); }); + mEmojiList->setDoubleClickCallback([this]() { onEmojiPick(); }); + mEmojiList->setVisible(!mUseGrid); + + mEmojiScroll = getChild<LLScrollContainer>("EmojiGridContainer"); + mEmojiScroll->setVisible(mUseGrid); + mEmojiScroll->setMouseEnterCallback([this](LLUICtrl*, const LLSD&) { onGridMouseEnter(); }); + mEmojiScroll->setMouseLeaveCallback([this](LLUICtrl*, const LLSD&) { onGridMouseLeave(); }); + + mEmojiGrid = getChild<LLScrollingPanelList>("EmojiGrid"); + fillEmojis(); return TRUE; } +void LLFloaterEmojiPicker::dirtyRect() +{ + super::dirtyRect(); + + if (mUseGrid && mEmojiScroll && mEmojiScroll->getRect().getWidth() != mRecentGridWidth) + { + fillEmojiGrid(); + } +} + LLFloaterEmojiPicker::~LLFloaterEmojiPicker() { gFocusMgr.releaseFocusIfNeeded( this ); @@ -152,7 +289,15 @@ LLFloaterEmojiPicker::~LLFloaterEmojiPicker() void LLFloaterEmojiPicker::fillEmojis() { - mEmojis->clearRows(); + if (mUseGrid) + fillEmojiGrid(); + else + fillEmojiList(); +} + +void LLFloaterEmojiPicker::fillEmojiList() +{ + mEmojiList->clearRows(); const LLEmojiDictionary::emoji2descr_map_t& emoji2Descr = LLEmojiDictionary::instance().getEmoji2Descr(); for (const LLEmojiDictionary::emoji2descr_item_t& item : emoji2Descr) @@ -169,17 +314,17 @@ void LLFloaterEmojiPicker::fillEmojis() // The following line adds default monochrome view of the emoji (is shown as an example) //params.columns.add().column("look").value(wstring_to_utf8str(LLWString(1, it.first))); params.columns.add().column("name").value(descr->Name); - mEmojis->addRow(new LLEmojiScrollListItem(item.first, params), params); + mEmojiList->addRow(new LLEmojiScrollListItem(item.first, params), params); } - if (mEmojis->getItemCount()) + if (mEmojiList->getItemCount()) { - if (mSelectedEmojiIndex > 0 && mSelectedEmojiIndex < mEmojis->getItemCount()) - mEmojis->selectNthItem(mSelectedEmojiIndex); + if (mSelectedEmojiIndex > 0 && mSelectedEmojiIndex < mEmojiList->getItemCount()) + mEmojiList->selectNthItem(mSelectedEmojiIndex); else - mEmojis->selectFirstItem(); + mEmojiList->selectFirstItem(); - mEmojis->scrollToShowSelected(); + mEmojiList->scrollToShowSelected(); } else { @@ -187,6 +332,103 @@ void LLFloaterEmojiPicker::fillEmojis() } } +void LLFloaterEmojiPicker::fillEmojiGrid() +{ + mEmojiGrid->clearPanels(); + + S32 scrollbarSize = mEmojiScroll->getSize(); + if (scrollbarSize < 0) + { + static LLUICachedControl<S32> scrollbar_size_control("UIScrollbarSize", 0); + scrollbarSize = scrollbar_size_control; + } + + mRecentGridWidth = mEmojiScroll->getRect().getWidth(); + const S32 clientWidth = mRecentGridWidth - scrollbarSize - mEmojiScroll->getBorderWidth() * 2; + if (mEmojiGrid->getRect().getWidth() != clientWidth) + { + LLRect rect = mEmojiGrid->getRect(); + rect.mRight = rect.mLeft + clientWidth; + mEmojiGrid->setRect(rect); + } + + const S32 gridPadding = mEmojiGrid->getPadding(); + const S32 iconSpacing = mEmojiGrid->getSpacing(); + const S32 rowWidth = clientWidth - gridPadding * 2; + const S32 iconSize = 28; // icon width and height + const S32 maxIcons = llmax(1, (rowWidth + iconSpacing) / (iconSize + iconSpacing)); + + LLPanel::Params row_panel_params; + row_panel_params.rect = LLRect(0, iconSize, rowWidth, 0); + + LLScrollingPanelList::Params row_list_params; + row_list_params.rect = row_panel_params.rect; + row_list_params.is_horizontal = TRUE; + row_list_params.padding = 0; + row_list_params.spacing = iconSpacing; + + LLPanel::Params icon_params; + LLRect icon_rect(0, iconSize, iconSize, 0); + + auto listCategory = [&](std::string category, const std::vector<const LLEmojiDescriptor*>& emojis, bool showDivider) + { + int iconIndex = 0; + LLEmojiGridRow* row = nullptr; + LLStringUtil::capitalize(category); + for (const LLEmojiDescriptor* descr : emojis) + { + if (mSearchPattern.empty() || matchesPattern(descr)) + { + // Place a category title if needed + if (showDivider) + { + LLEmojiGridDivider* div = new LLEmojiGridDivider(row_panel_params, category); + mEmojiGrid->addPanel(div, true); + showDivider = false; + } + + // Place a new row each (maxIcons) icons + if (!(iconIndex % maxIcons)) + { + row = new LLEmojiGridRow(row_panel_params, row_list_params); + mEmojiGrid->addPanel(row, true); + } + + // Place a new icon to the current row + LLEmojiGridIcon* icon = new LLEmojiGridIcon(icon_params, descr, category); + icon->setMouseEnterCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseEnter(ctrl); }); + icon->setMouseLeaveCallback([this](LLUICtrl* ctrl, const LLSD&) { onEmojiMouseLeave(ctrl); }); + icon->setMouseUpCallback([this](LLUICtrl* ctrl, S32, S32, MASK mask) { onEmojiMouseClick(ctrl, mask); }); + icon->setRect(icon_rect); + row->mList->addPanel(icon, true); + + iconIndex++; + } + } + }; + + const LLEmojiDictionary::cat2descrs_map_t& category2Descr = LLEmojiDictionary::instance().getCategory2Descrs(); + if (mSelectedCategory.empty()) + { + // List all categories with titles + for (const LLEmojiDictionary::cat2descrs_item_t& item : category2Descr) + { + listCategory(item.first, item.second, TRUE); + } + } + else + { + // List one category without title + const LLEmojiDictionary::cat2descrs_map_t::const_iterator& item = category2Descr.find(mSelectedCategory); + if (item != category2Descr.end()) + { + listCategory(mSelectedCategory, item->second, FALSE); + } + } + + onEmojiEmpty(); +} + bool LLFloaterEmojiPicker::matchesCategory(const LLEmojiDescriptor* descr) { return std::find(descr->Categories.begin(), descr->Categories.end(), mSelectedCategory) != descr->Categories.end(); @@ -223,19 +465,85 @@ void LLFloaterEmojiPicker::onPreviewEmojiClick() { if (mEmojiPickCallback) { - if (LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojis->getFirstSelected())) + if (mUseGrid) { - mEmojiPickCallback(item->getEmoji()); + if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(mHoveredIcon)) + { + mEmojiPickCallback(icon->getEmoji()); + } + } + else + { + if (LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojiList->getFirstSelected())) + { + mEmojiPickCallback(item->getEmoji()); + } + } + } +} + +void LLFloaterEmojiPicker::onGridMouseEnter() +{ + mSearch->setVisible(FALSE); + mDescription->setText(LLStringExplicit(""), LLStyle::Params()); + mDescription->setVisible(TRUE); +} + +void LLFloaterEmojiPicker::onGridMouseLeave() +{ + mDescription->setVisible(FALSE); + mDescription->setText(LLStringExplicit(""), LLStyle::Params()); + mSearch->setVisible(TRUE); + mSearch->setFocus(TRUE); +} + +void LLFloaterEmojiPicker::onEmojiMouseEnter(LLUICtrl* ctrl) +{ + if (ctrl) + { + if (mHoveredIcon && mHoveredIcon != ctrl) + { + unselectGridIcon(mHoveredIcon); + } + + selectGridIcon(ctrl); + + mHoveredIcon = ctrl; + } +} + +void LLFloaterEmojiPicker::onEmojiMouseLeave(LLUICtrl* ctrl) +{ + if (ctrl) + { + if (ctrl == mHoveredIcon) + { + unselectGridIcon(ctrl); + } + } +} + +void LLFloaterEmojiPicker::onEmojiMouseClick(LLUICtrl* ctrl, MASK mask) +{ + if (mEmojiPickCallback) + { + if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) + { + mPreviewEmoji->handleAnyMouseClick(0, 0, 0, EMouseClickType::CLICK_LEFT, TRUE); + mPreviewEmoji->handleAnyMouseClick(0, 0, 0, EMouseClickType::CLICK_LEFT, FALSE); + if (!(mask & 4)) + { + closeFloater(); + } } } } void LLFloaterEmojiPicker::onEmojiSelect() { - const LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojis->getFirstSelected()); - if (item) + if (LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojiList->getFirstSelected())) { - mSelectedEmojiIndex = mEmojis->getFirstSelectedIndex(); + mSelectedEmojiIndex = mEmojiList->getFirstSelectedIndex(); LLUIString text; text.insert(0, LLWString(1, item->getEmoji())); mPreviewEmoji->setLabel(text); @@ -255,7 +563,7 @@ void LLFloaterEmojiPicker::onEmojiPick() { if (mEmojiPickCallback) { - if (LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojis->getFirstSelected())) + if (LLEmojiScrollListItem* item = dynamic_cast<LLEmojiScrollListItem*>(mEmojiList->getFirstSelected())) { mEmojiPickCallback(item->getEmoji()); closeFloater(); @@ -263,6 +571,31 @@ void LLFloaterEmojiPicker::onEmojiPick() } } +void LLFloaterEmojiPicker::selectGridIcon(LLUICtrl* ctrl) +{ + if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) + { + icon->setBackgroundVisible(TRUE); + + LLUIString text; + text.insert(0, icon->getText()); + mPreviewEmoji->setLabel(text); + + std::string descr = icon->getDescr() + "\n" + icon->getCategory(); + mDescription->setText(LLStringExplicit(descr), LLStyle::Params()); + } +} + +void LLFloaterEmojiPicker::unselectGridIcon(LLUICtrl* ctrl) +{ + if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl)) + { + icon->setBackgroundVisible(FALSE); + mPreviewEmoji->setLabel(LLUIString()); + mDescription->setText(LLStringExplicit(""), LLStyle::Params()); + } +} + // virtual BOOL LLFloaterEmojiPicker::handleKeyHere(KEY key, MASK mask) { @@ -279,12 +612,12 @@ BOOL LLFloaterEmojiPicker::handleKeyHere(KEY key, MASK mask) closeFloater(); return TRUE; case KEY_UP: - mEmojis->selectPrevItem(); - mEmojis->scrollToShowSelected(); + mEmojiList->selectPrevItem(); + mEmojiList->scrollToShowSelected(); return TRUE; case KEY_DOWN: - mEmojis->selectNextItem(); - mEmojis->scrollToShowSelected(); + mEmojiList->selectNextItem(); + mEmojiList->scrollToShowSelected(); return TRUE; } } @@ -297,5 +630,7 @@ 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 7327fb945e..ba71f04830 100644 --- a/indra/newview/llfloateremojipicker.h +++ b/indra/newview/llfloateremojipicker.h @@ -33,6 +33,8 @@ struct LLEmojiDescriptor; class LLFloaterEmojiPicker : public LLFloater { + using super = LLFloater; + public: // The callback function will be called with an emoji char. typedef boost::function<void (llwchar)> pick_callback_t; @@ -45,7 +47,8 @@ public: LLFloaterEmojiPicker(const LLSD& key); virtual ~LLFloaterEmojiPicker(); - virtual BOOL postBuild(); + virtual BOOL postBuild() override; + virtual void dirtyRect() override; void show(pick_callback_t pick_callback = nullptr, close_callback_t close_callback = nullptr); @@ -53,29 +56,47 @@ public: private: void fillEmojis(); + void fillEmojiList(); + void fillEmojiGrid(); + bool matchesCategory(const LLEmojiDescriptor* descr); bool matchesPattern(const LLEmojiDescriptor* descr); void onCategoryCommit(); void onSearchKeystroke(); void onPreviewEmojiClick(); + void onGridMouseEnter(); + void onGridMouseLeave(); + void onEmojiMouseEnter(LLUICtrl* ctrl); + void onEmojiMouseLeave(LLUICtrl* ctrl); + void onEmojiMouseClick(LLUICtrl* ctrl, MASK mask); void onEmojiSelect(); void onEmojiEmpty(); void onEmojiPick(); - virtual BOOL handleKeyHere(KEY key, MASK mask); + void selectGridIcon(LLUICtrl* ctrl); + void unselectGridIcon(LLUICtrl* ctrl); + + virtual BOOL handleKeyHere(KEY key, MASK mask) override; class LLComboBox* mCategory { nullptr }; class LLLineEditor* mSearch { nullptr }; - class LLScrollListCtrl* mEmojis { nullptr }; + class LLScrollListCtrl* mEmojiList { nullptr }; + class LLScrollContainer* mEmojiScroll { nullptr }; + class LLScrollingPanelList* mEmojiGrid { nullptr }; class LLButton* mPreviewEmoji { nullptr }; + class LLTextBox* mDescription { nullptr }; pick_callback_t mEmojiPickCallback; close_callback_t mFloaterCloseCallback; + S32 mRecentGridWidth { 0 }; + LLUICtrl* mHoveredIcon { nullptr }; + static std::string mSelectedCategory; static std::string mSearchPattern; static int mSelectedEmojiIndex; + static bool mUseGrid; }; #endif diff --git a/indra/newview/skins/default/xui/en/floater_activeim.xml b/indra/newview/skins/default/xui/en/floater_activeim.xml index b79c5d9a19..42c3e7e935 100644 --- a/indra/newview/skins/default/xui/en/floater_activeim.xml +++ b/indra/newview/skins/default/xui/en/floater_activeim.xml @@ -23,7 +23,7 @@ <scrolling_panel_list follows="left|right" layout="topleft" - left="1" + left="1" name="chiclet_row_panel_list" width="318"/> </scroll_container> 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 000d779759..76cd1c6d2a 100644 --- a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml +++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml @@ -20,6 +20,15 @@ left="34" height="29" width="162" /> + <text + name="Description" + layout="bottomleft" + follows="bottom|left|right" + font="SansSerifMedium" + bottom="14" + left="42" + height="29" + width="150" /> <button name="PreviewEmoji" layout="bottomleft" @@ -31,7 +40,7 @@ height="29" width="29" /> <scroll_list - name="Emojis" + name="EmojiList" layout="topleft" follows="all" sort_column="0" @@ -52,6 +61,24 @@ label="Name" name="name" /> </scroll_list> + <scroll_container + name="EmojiGridContainer" + layout="topleft" + follows="all" + top="25" + left="0" + height="330" + width="200"> + <scrolling_panel_list + name="EmojiGrid" + layout="topleft" + follows="top|left|right" + padding="4" + spacing="0" + top="0" + left="0" + width="200"/> + </scroll_container> <combo_box name="Category" label="Choose a category" |