SL-20356 EmojiPicker - Implement arrow keys navigation
parent
2e0892e530
commit
716b9af35d
|
|
@ -70,6 +70,7 @@ LLScrollContainer::Params::Params()
|
|||
bg_color("color"),
|
||||
border_visible("border_visible"),
|
||||
hide_scrollbar("hide_scrollbar"),
|
||||
ignore_arrow_keys("ignore_arrow_keys"),
|
||||
min_auto_scroll_rate("min_auto_scroll_rate", 100),
|
||||
max_auto_scroll_rate("max_auto_scroll_rate", 1000),
|
||||
max_auto_scroll_zone("max_auto_scroll_zone", 16),
|
||||
|
|
@ -86,6 +87,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
|
|||
mBackgroundColor(p.bg_color()),
|
||||
mIsOpaque(p.is_opaque),
|
||||
mHideScrollbar(p.hide_scrollbar),
|
||||
mIgnoreArrowKeys(p.ignore_arrow_keys),
|
||||
mReserveScrollCorner(p.reserve_scroll_corner),
|
||||
mMinAutoScrollRate(p.min_auto_scroll_rate),
|
||||
mMaxAutoScrollRate(p.max_auto_scroll_rate),
|
||||
|
|
@ -204,10 +206,29 @@ void LLScrollContainer::reshape(S32 width, S32 height,
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
|
||||
{
|
||||
if (mIgnoreArrowKeys)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case KEY_LEFT:
|
||||
case KEY_RIGHT:
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
case KEY_PAGE_UP:
|
||||
case KEY_PAGE_DOWN:
|
||||
case KEY_HOME:
|
||||
case KEY_END:
|
||||
return FALSE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// allow scrolled view to handle keystrokes in case it delegated keyboard focus
|
||||
// to the scroll container.
|
||||
// to the scroll container.
|
||||
// NOTE: this should not recurse indefinitely as handleKeyHere
|
||||
// should not propagate to parent controls, so mScrolledView should *not*
|
||||
// call LLScrollContainer::handleKeyHere in turn
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ public:
|
|||
Optional<bool> is_opaque,
|
||||
reserve_scroll_corner,
|
||||
border_visible,
|
||||
hide_scrollbar;
|
||||
hide_scrollbar,
|
||||
ignore_arrow_keys;
|
||||
Optional<F32> min_auto_scroll_rate,
|
||||
max_auto_scroll_rate;
|
||||
Optional<U32> max_auto_scroll_zone;
|
||||
|
|
@ -149,6 +150,7 @@ private:
|
|||
F32 mMaxAutoScrollRate;
|
||||
U32 mMaxAutoScrollZone;
|
||||
bool mHideScrollbar;
|
||||
bool mIgnoreArrowKeys;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public:
|
|||
|
||||
|
||||
/*
|
||||
* A set of panels that are displayed in a vertical sequence inside a scroll container.
|
||||
* A set of panels that are displayed in a sequence inside a scroll container.
|
||||
*/
|
||||
class LLScrollingPanelList : public LLUICtrl
|
||||
{
|
||||
|
|
|
|||
|
|
@ -399,6 +399,13 @@ void LLFloaterEmojiPicker::moveGroups()
|
|||
mBadge->setRect(rect);
|
||||
}
|
||||
|
||||
void LLFloaterEmojiPicker::showPreview(bool show)
|
||||
{
|
||||
mPreview->setEmoji(nullptr);
|
||||
mDummy->setVisible(show ? FALSE : TRUE);
|
||||
mPreview->setVisible(show ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
void LLFloaterEmojiPicker::fillEmojis(bool fromResize)
|
||||
{
|
||||
mRecentGridWidth = mEmojiScroll->getRect().getWidth();
|
||||
|
|
@ -423,6 +430,9 @@ void LLFloaterEmojiPicker::fillEmojis(bool fromResize)
|
|||
|
||||
mRecentMaxIcons = maxIcons;
|
||||
|
||||
mFocusedIconRow = 0;
|
||||
mFocusedIconCol = 0;
|
||||
mFocusedIcon = nullptr;
|
||||
mHoveredIcon = nullptr;
|
||||
mEmojiGrid->clearPanels();
|
||||
mPreview->setEmoji(nullptr);
|
||||
|
|
@ -599,15 +609,17 @@ void LLFloaterEmojiPicker::onFilterChanged()
|
|||
|
||||
void LLFloaterEmojiPicker::onGridMouseEnter()
|
||||
{
|
||||
mDummy->setVisible(FALSE);
|
||||
mPreview->setEmoji(nullptr);
|
||||
mPreview->setVisible(TRUE);
|
||||
LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
|
||||
if (focus == mEmojiGrid)
|
||||
{
|
||||
exitArrowMode();
|
||||
}
|
||||
showPreview(true);
|
||||
}
|
||||
|
||||
void LLFloaterEmojiPicker::onGridMouseLeave()
|
||||
{
|
||||
mPreview->setVisible(FALSE);
|
||||
mDummy->setVisible(TRUE);
|
||||
showPreview(false);
|
||||
}
|
||||
|
||||
void LLFloaterEmojiPicker::onGroupButtonMouseEnter(LLUICtrl* ctrl)
|
||||
|
|
@ -630,6 +642,13 @@ void LLFloaterEmojiPicker::onEmojiMouseEnter(LLUICtrl* ctrl)
|
|||
{
|
||||
if (ctrl)
|
||||
{
|
||||
LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
|
||||
if (focus == mEmojiGrid)
|
||||
{
|
||||
exitArrowMode();
|
||||
showPreview(true);
|
||||
}
|
||||
|
||||
if (mHoveredIcon && mHoveredIcon != ctrl)
|
||||
{
|
||||
unselectGridIcon(mHoveredIcon);
|
||||
|
|
@ -679,6 +698,113 @@ void LLFloaterEmojiPicker::onEmojiMouseUp(LLUICtrl* ctrl)
|
|||
}
|
||||
}
|
||||
|
||||
bool LLFloaterEmojiPicker::enterArrowMode()
|
||||
{
|
||||
S32 rowCount = mEmojiGrid->getPanelList().size();
|
||||
if (rowCount)
|
||||
{
|
||||
mFocusedIconRow = -1;
|
||||
mFocusedIconCol = 0;
|
||||
if (moveFocusedIconDown())
|
||||
{
|
||||
showPreview(true);
|
||||
mEmojiScroll->goToTop();
|
||||
mEmojiGrid->setFocus(TRUE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LLFloaterEmojiPicker::exitArrowMode()
|
||||
{
|
||||
if (mFocusedIcon)
|
||||
{
|
||||
unselectGridIcon(mFocusedIcon);
|
||||
mFocusedIcon = nullptr;
|
||||
}
|
||||
|
||||
showPreview(false);
|
||||
mEmojiScroll->goToTop();
|
||||
mFocusedIconRow = mFocusedIconCol = 0;
|
||||
mFilter->setFocus(TRUE);
|
||||
}
|
||||
|
||||
void LLFloaterEmojiPicker::selectFocusedIcon()
|
||||
{
|
||||
if (mHoveredIcon)
|
||||
{
|
||||
unselectGridIcon(mHoveredIcon);
|
||||
}
|
||||
|
||||
if (mFocusedIcon)
|
||||
{
|
||||
unselectGridIcon(mFocusedIcon);
|
||||
}
|
||||
|
||||
// Both mFocusedIconRow and mFocusedIconCol should be already verified
|
||||
LLEmojiGridRow* row = (LLEmojiGridRow*)mEmojiGrid->getPanelList()[mFocusedIconRow];
|
||||
mFocusedIcon = row->mList->getPanelList()[mFocusedIconCol];
|
||||
selectGridIcon(mFocusedIcon);
|
||||
}
|
||||
|
||||
bool LLFloaterEmojiPicker::moveFocusedIconUp()
|
||||
{
|
||||
for (S32 i = mFocusedIconRow - 1; i >= 0; --i)
|
||||
{
|
||||
LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i];
|
||||
LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel);
|
||||
if (row && row->mList->getPanelList().size() > mFocusedIconCol)
|
||||
{
|
||||
mEmojiScroll->scrollToShowRect(row->getBoundingRect());
|
||||
mFocusedIconRow = i;
|
||||
selectFocusedIcon();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLFloaterEmojiPicker::moveFocusedIconDown()
|
||||
{
|
||||
S32 rowCount = mEmojiGrid->getPanelList().size();
|
||||
for (S32 i = mFocusedIconRow + 1; i < rowCount; ++i)
|
||||
{
|
||||
LLScrollingPanel* panel = mEmojiGrid->getPanelList()[i];
|
||||
LLEmojiGridRow* row = dynamic_cast<LLEmojiGridRow*>(panel);
|
||||
if (row && row->mList->getPanelList().size() > mFocusedIconCol)
|
||||
{
|
||||
mEmojiScroll->scrollToShowRect(row->getBoundingRect());
|
||||
mFocusedIconRow = i;
|
||||
selectFocusedIcon();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLFloaterEmojiPicker::moveFocusedIconLeft()
|
||||
{
|
||||
if (mFocusedIconCol <= 0)
|
||||
return false;
|
||||
|
||||
mFocusedIconCol--;
|
||||
selectFocusedIcon();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLFloaterEmojiPicker::moveFocusedIconRight()
|
||||
{
|
||||
LLEmojiGridRow* row = (LLEmojiGridRow*)mEmojiGrid->getPanelList()[mFocusedIconRow];
|
||||
S32 colCount = row->mList->getPanelList().size();
|
||||
if (mFocusedIconCol >= colCount - 1)
|
||||
return false;
|
||||
|
||||
mFocusedIconCol++;
|
||||
selectFocusedIcon();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLFloaterEmojiPicker::selectGridIcon(LLUICtrl* ctrl)
|
||||
{
|
||||
if (LLEmojiGridIcon* icon = dynamic_cast<LLEmojiGridIcon*>(ctrl))
|
||||
|
|
@ -697,6 +823,64 @@ void LLFloaterEmojiPicker::unselectGridIcon(LLUICtrl* ctrl)
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
BOOL LLFloaterEmojiPicker::handleKey(KEY key, MASK mask, BOOL called_from_parent)
|
||||
{
|
||||
if (mask == MASK_NONE)
|
||||
{
|
||||
LLFocusableElement* focus = gFocusMgr.getKeyboardFocus();
|
||||
if (focus == mEmojiGrid)
|
||||
{
|
||||
if (key == KEY_RETURN)
|
||||
{
|
||||
if (mFocusedIcon)
|
||||
{
|
||||
onEmojiMouseDown(mFocusedIcon);
|
||||
onEmojiMouseUp(mFocusedIcon);
|
||||
closeFloater();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else if (key == KEY_TAB)
|
||||
{
|
||||
exitArrowMode();
|
||||
return TRUE;
|
||||
}
|
||||
else if (key == KEY_UP)
|
||||
{
|
||||
if (!moveFocusedIconUp())
|
||||
exitArrowMode();
|
||||
return TRUE;
|
||||
}
|
||||
else if (key == KEY_DOWN)
|
||||
{
|
||||
if (moveFocusedIconDown())
|
||||
return TRUE;
|
||||
}
|
||||
else if (key == KEY_LEFT)
|
||||
{
|
||||
if (moveFocusedIconLeft())
|
||||
return TRUE;
|
||||
}
|
||||
else if (key == KEY_RIGHT)
|
||||
{
|
||||
if (moveFocusedIconRight())
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else // if (focus != mEmojiGrid)
|
||||
{
|
||||
if (key == KEY_DOWN)
|
||||
{
|
||||
if (enterArrowMode())
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super::handleKey(key, mask, called_from_parent);
|
||||
}
|
||||
|
||||
// virtual
|
||||
BOOL LLFloaterEmojiPicker::handleKeyHere(KEY key, MASK mask)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ public:
|
|||
private:
|
||||
void fillGroups();
|
||||
void moveGroups();
|
||||
void showPreview(bool show);
|
||||
void fillEmojis(bool fromResize = false);
|
||||
|
||||
void onGroupButtonClick(LLUICtrl* ctrl);
|
||||
|
|
@ -76,9 +77,18 @@ private:
|
|||
void onEmojiMouseDown(LLUICtrl* ctrl);
|
||||
void onEmojiMouseUp(LLUICtrl* ctrl);
|
||||
|
||||
bool enterArrowMode();
|
||||
void exitArrowMode();
|
||||
void selectFocusedIcon();
|
||||
bool moveFocusedIconUp();
|
||||
bool moveFocusedIconDown();
|
||||
bool moveFocusedIconLeft();
|
||||
bool moveFocusedIconRight();
|
||||
|
||||
void selectGridIcon(LLUICtrl* ctrl);
|
||||
void unselectGridIcon(LLUICtrl* ctrl);
|
||||
|
||||
virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent) override;
|
||||
virtual BOOL handleKeyHere(KEY key, MASK mask) override;
|
||||
|
||||
class LLPanel* mGroups { nullptr };
|
||||
|
|
@ -97,6 +107,9 @@ private:
|
|||
S32 mRecentBadgeWidth { 0 };
|
||||
S32 mRecentGridWidth { 0 };
|
||||
S32 mRecentMaxIcons { 0 };
|
||||
S32 mFocusedIconRow { 0 };
|
||||
S32 mFocusedIconCol { 0 };
|
||||
LLUICtrl* mFocusedIcon { nullptr };
|
||||
LLUICtrl* mHoveredIcon { nullptr };
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3054,6 +3054,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
|
|||
case KEY_PAGE_UP:
|
||||
case KEY_PAGE_DOWN:
|
||||
case KEY_HOME:
|
||||
case KEY_END:
|
||||
// when chatbar is empty or ArrowKeysAlwaysMove set,
|
||||
// pass arrow keys on to avatar...
|
||||
return FALSE;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
name="EmojiGridContainer"
|
||||
layout="topleft"
|
||||
follows="all"
|
||||
ignore_arrow_keys="true"
|
||||
top="50"
|
||||
left="0"
|
||||
height="300"
|
||||
|
|
|
|||
Loading…
Reference in New Issue