SL-19575 Rework emoji picker layout similar to Slack
parent
886c608967
commit
8bbbce015b
|
|
@ -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,36 +111,7 @@ void LLScrollingPanelList::removePanel( U32 panel_index )
|
|||
mPanelList.erase( mPanelList.begin() + panel_index );
|
||||
}
|
||||
|
||||
const S32 GAP_BETWEEN_PANELS = 6;
|
||||
|
||||
// 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)
|
||||
{
|
||||
LLScrollingPanel *childp = *iter;
|
||||
total_height += childp->getRect().getHeight() + cur_gap;
|
||||
max_width = llmax( max_width, childp->getRect().getWidth() );
|
||||
cur_gap = GAP_BETWEEN_PANELS;
|
||||
}
|
||||
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)
|
||||
{
|
||||
LLScrollingPanel *childp = *iter;
|
||||
cur_y -= childp->getRect().getHeight();
|
||||
childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom);
|
||||
cur_y -= GAP_BETWEEN_PANELS;
|
||||
}
|
||||
rearrange();
|
||||
}
|
||||
|
||||
void LLScrollingPanelList::updatePanels(BOOL allow_modify)
|
||||
|
|
@ -162,20 +124,91 @@ void LLScrollingPanelList::updatePanels(BOOL 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();
|
||||
if (mIsHorizontal || !followsRight())
|
||||
{
|
||||
rc.mRight = rc.mLeft + new_width;
|
||||
}
|
||||
if (!mIsHorizontal || !followsBottom())
|
||||
{
|
||||
rc.mBottom = rc.mTop - new_height;
|
||||
}
|
||||
|
||||
if (rc.mRight != getRect().mRight || rc.mBottom != getRect().mBottom)
|
||||
{
|
||||
setRect(rc);
|
||||
notifySizeChanged();
|
||||
}
|
||||
|
||||
// Reposition each of the child views
|
||||
S32 pos = mIsHorizontal ? mPadding : rc.getHeight() - mPadding;
|
||||
for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
|
||||
iter != mPanelList.end(); ++iter)
|
||||
{
|
||||
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::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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue