diff options
author | Alexander Gavriliuk <alexandrgproductengine@lindenlab.com> | 2023-11-07 23:13:00 +0100 |
---|---|---|
committer | Guru <alexandrgproductengine@lindenlab.com> | 2023-11-08 11:15:35 +0100 |
commit | 716b9af35d0dbc3ba8048a1bcca2c8c8f703bed0 (patch) | |
tree | 3cc496a6dd1e810b8ad5fafbb2a3797b2ce20258 | |
parent | 2e0892e53041f75fc991b77870a4f2f4672fefb1 (diff) |
SL-20356 EmojiPicker - Implement arrow keys navigation
-rw-r--r-- | indra/llui/llscrollcontainer.cpp | 23 | ||||
-rw-r--r-- | indra/llui/llscrollcontainer.h | 4 | ||||
-rw-r--r-- | indra/llui/llscrollingpanellist.h | 2 | ||||
-rw-r--r-- | indra/newview/llfloateremojipicker.cpp | 194 | ||||
-rw-r--r-- | indra/newview/llfloateremojipicker.h | 13 | ||||
-rw-r--r-- | indra/newview/llviewerwindow.cpp | 1 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_emoji_picker.xml | 1 |
7 files changed, 230 insertions, 8 deletions
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index ad32f7186c..22d27b1f2a 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -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 diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index dacea2a987..79dc70cac9 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -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; }; diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h index 9dc65dabb5..d625039427 100644 --- a/indra/llui/llscrollingpanellist.h +++ b/indra/llui/llscrollingpanellist.h @@ -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 { diff --git a/indra/newview/llfloateremojipicker.cpp b/indra/newview/llfloateremojipicker.cpp index 10013c907d..45c33728e5 100644 --- a/indra/newview/llfloateremojipicker.cpp +++ b/indra/newview/llfloateremojipicker.cpp @@ -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)) @@ -698,6 +824,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) { if (mask == MASK_NONE) diff --git a/indra/newview/llfloateremojipicker.h b/indra/newview/llfloateremojipicker.h index b8e3190bf3..48f66d7950 100644 --- a/indra/newview/llfloateremojipicker.h +++ b/indra/newview/llfloateremojipicker.h @@ -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 }; }; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 39b4075596..ad177cdb85 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -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; 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 e15c304851..a89d97d8e9 100644 --- a/indra/newview/skins/default/xui/en/floater_emoji_picker.xml +++ b/indra/newview/skins/default/xui/en/floater_emoji_picker.xml @@ -25,6 +25,7 @@ name="EmojiGridContainer" layout="topleft" follows="all" + ignore_arrow_keys="true" top="50" left="0" height="300" |