summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Gavriliuk <alexandrgproductengine@lindenlab.com>2023-07-04 07:38:05 +0200
committerGuru <alexandrgproductengine@lindenlab.com>2023-07-05 10:07:19 +0200
commit8bbbce015b6dae1bdafe0bba329463322642ca85 (patch)
tree82b01abfaad33f9bdd3586d50bf045c91b84953b
parent886c6089672cc24a4ce5d2f84f388087e7d6281b (diff)
SL-19575 Rework emoji picker layout similar to Slack
-rw-r--r--indra/llui/llscrollingpanellist.cpp184
-rw-r--r--indra/llui/llscrollingpanellist.h37
-rw-r--r--indra/newview/llfloateremojipicker.cpp375
-rw-r--r--indra/newview/llfloateremojipicker.h27
-rw-r--r--indra/newview/skins/default/xui/en/floater_activeim.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_emoji_picker.xml29
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"