summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorEugene Mutavchi <emutavchi@productengine.com>2009-11-20 20:27:53 +0200
committerEugene Mutavchi <emutavchi@productengine.com>2009-11-20 20:27:53 +0200
commitc9101f603a7969f4d46a7d8d911fa8805d926548 (patch)
tree5245dba3a5721dc1f3dd9a6b6cb979836ed75248 /indra
parent0c468557b9e38ec4be201fa991499ed095a4d8c3 (diff)
Implemented low task EXT-1153(FlatListView should support keyboard)
--HG-- branch : product-engine
Diffstat (limited to 'indra')
-rw-r--r--indra/llui/llflatlistview.cpp241
-rw-r--r--indra/llui/llflatlistview.h25
-rw-r--r--indra/newview/llpanelpeople.cpp7
-rw-r--r--indra/newview/llparticipantlist.cpp3
4 files changed, 276 insertions, 0 deletions
diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
index 19f203b80c..8de3a8a96f 100644
--- a/indra/llui/llflatlistview.cpp
+++ b/indra/llui/llflatlistview.cpp
@@ -94,6 +94,9 @@ bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*
item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+ // Children don't accept the focus
+ item->setTabStop(false);
+
rearrangeItems();
notifyParentItemsRectChanged();
return true;
@@ -282,6 +285,9 @@ void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/)
{
onCommit();
}
+
+ // Stretch selected items rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getSelectedItemsRect().stretch(-1));
}
void LLFlatListView::setNoItemsCommentText(const std::string& comment_text)
@@ -381,8 +387,34 @@ LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
//we don't need to stretch in vertical direction on reshaping by a parent
//no bottom following!
mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
+
+ LLViewBorder::Params params;
+ params.name("scroll border");
+ params.rect(getSelectedItemsRect());
+ params.visible(false);
+ params.bevel_style(LLViewBorder::BEVEL_IN);
+ mSelectedItemsBorder = LLUICtrlFactory::create<LLViewBorder> (params);
+ mItemsPanel->addChild( mSelectedItemsBorder );
};
+// virtual
+void LLFlatListView::draw()
+{
+ // Highlight border if a child of this container has keyboard focus
+ if( mSelectedItemsBorder->getVisible() )
+ {
+ mSelectedItemsBorder->setKeyboardFocusHighlight( hasFocus() );
+ }
+ LLScrollContainer::draw();
+}
+
+// virtual
+BOOL LLFlatListView::postBuild()
+{
+ setTabStop(true);
+ return LLScrollContainer::postBuild();
+}
+
void LLFlatListView::rearrangeItems()
{
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
@@ -444,6 +476,9 @@ void LLFlatListView::rearrangeItems()
// move top for next item in list
item_new_top -= (rc.getHeight() + mItemPad);
}
+
+ // Stretch selected items rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getSelectedItemsRect().stretch(-1));
}
void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
@@ -473,6 +508,64 @@ void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask)
onItemMouseClick(item_pair, mask);
}
+BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask)
+{
+ BOOL reset_selection = (mask != MASK_SHIFT);
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_RETURN:
+ {
+ if (mSelectedItemPairs.size() && mask == MASK_NONE)
+ {
+ mOnReturnSignal(this, getValue());
+ handled = TRUE;
+ }
+ break;
+ }
+ case KEY_UP:
+ {
+ if ( !selectNextItemPair(true, reset_selection) && reset_selection)
+ {
+ // If case we are in accordion tab notify parent to go to the previous accordion
+ notifyParent(LLSD().insert("action","select_prev"));
+ }
+ break;
+ }
+ case KEY_DOWN:
+ {
+ if ( !selectNextItemPair(false, reset_selection) && reset_selection)
+ {
+ // If case we are in accordion tab notify parent to go to the next accordion
+ notifyParent(LLSD().insert("action","select_next"));
+ }
+ break;
+ }
+ case 'A':
+ {
+ if(MASK_CONTROL & mask)
+ {
+ selectAll();
+ handled = TRUE;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if ( key == KEY_UP || key == KEY_DOWN )
+ {
+ LLRect selcted_rect = getLastSelectedItemRect().stretch(1);
+ LLRect visible_rect = getVisibleContentRect();
+ if ( !visible_rect.contains (selcted_rect) )
+ scrollToShowRect(selcted_rect);
+ handled = TRUE;
+ }
+
+ return handled ? handled : LLScrollContainer::handleKeyHere(key, mask);
+}
+
LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const
{
llassert(item);
@@ -552,6 +645,143 @@ bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select)
onCommit();
}
+ setFocus(TRUE);
+
+ // Stretch selected items rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getSelectedItemsRect().stretch(-1));
+
+ return true;
+}
+
+LLRect LLFlatListView::getLastSelectedItemRect()
+{
+ if (!mSelectedItemPairs.size())
+ {
+ return LLRect::null;
+ }
+
+ return mSelectedItemPairs.back()->first->getRect();
+}
+
+LLRect LLFlatListView::getSelectedItemsRect()
+{
+ if (!mSelectedItemPairs.size())
+ {
+ return LLRect::null;
+ }
+ LLRect rc = getLastSelectedItemRect();
+ for ( pairs_const_iterator_t
+ it = mSelectedItemPairs.begin(),
+ it_end = mSelectedItemPairs.end();
+ it != it_end; ++it )
+ {
+ rc.unionWith((*it)->first->getRect());
+ }
+ return rc;
+}
+
+// virtual
+bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection)
+{
+ // No items - no actions!
+ if ( !mItemPairs.size() )
+ return false;
+
+ item_pair_t* cur_sel_pair = NULL;
+ item_pair_t* to_sel_pair = NULL;
+
+ if ( mSelectedItemPairs.size() )
+ {
+ // Take the last selected pair
+ cur_sel_pair = mSelectedItemPairs.back();
+ }
+ else
+ {
+ // If there weren't selected items then choose the first one bases on given direction
+ cur_sel_pair = (is_up_direction) ? mItemPairs.back() : mItemPairs.front();
+ // Force selection to first item
+ to_sel_pair = cur_sel_pair;
+ }
+
+ // Bases on given direction choose next item to select
+ if ( is_up_direction )
+ {
+ // Find current selected item position in mItemPairs list
+ pairs_list_t::reverse_iterator sel_it = std::find(mItemPairs.rbegin(), mItemPairs.rend(), cur_sel_pair);
+
+ for (;++sel_it != mItemPairs.rend();)
+ {
+ // skip invisible items
+ if ( (*sel_it)->first->getVisible() )
+ {
+ to_sel_pair = *sel_it;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Find current selected item position in mItemPairs list
+ pairs_list_t::iterator sel_it = std::find(mItemPairs.begin(), mItemPairs.end(), cur_sel_pair);
+
+ for (;++sel_it != mItemPairs.end();)
+ {
+ // skip invisible items
+ if ( (*sel_it)->first->getVisible() )
+ {
+ to_sel_pair = *sel_it;
+ break;
+ }
+ }
+ }
+
+ if ( to_sel_pair )
+ {
+ bool select = true;
+
+ if ( reset_selection )
+ {
+ // Reset current selection if we were asked about it
+ resetSelection();
+ }
+ else
+ {
+ // If item already selected and no reset request than we should deselect last selected item.
+ select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair));
+ }
+
+ // Select/Deselect next item
+ selectItemPair(select ? to_sel_pair : cur_sel_pair, select);
+
+ return true;
+ }
+ return false;
+}
+
+bool LLFlatListView::selectAll()
+{
+ if (!mAllowSelection)
+ return false;
+
+ mSelectedItemPairs.clear();
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ mSelectedItemPairs.push_back(item_pair);
+ //a way of notifying panel of selection state changes
+ LLPanel* item = item_pair->first;
+ item->setValue(SELECTED_EVENT);
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ // Stretch selected items rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getSelectedItemsRect().stretch(-1));
+
return true;
}
@@ -670,4 +900,15 @@ void LLFlatListView::getValues(std::vector<LLSD>& values) const
}
}
+// virtual
+void LLFlatListView::onFocusReceived()
+{
+ mSelectedItemsBorder->setVisible(TRUE);
+}
+// virtual
+void LLFlatListView::onFocusLost()
+{
+ mSelectedItemsBorder->setVisible(FALSE);
+}
+
//EOF
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
index 97772bc677..eac947a0d7 100644
--- a/indra/llui/llflatlistview.h
+++ b/indra/llui/llflatlistview.h
@@ -113,6 +113,10 @@ public:
virtual ~LLFlatListView() { clear(); };
+ /**
+ * Connects callback to signal called when Return key is pressed.
+ */
+ boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); }
/** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
@@ -318,6 +322,10 @@ protected:
virtual bool selectItemPair(item_pair_t* item_pair, bool select);
+ virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection);
+
+ virtual bool selectAll();
+
virtual bool isSelected(item_pair_t* item_pair) const;
virtual bool removeItemPair(item_pair_t* item_pair);
@@ -331,6 +339,19 @@ protected:
*/
void notifyParentItemsRectChanged();
+ virtual BOOL handleKeyHere(KEY key, MASK mask);
+
+ virtual BOOL postBuild();
+
+ virtual void onFocusReceived();
+
+ virtual void onFocusLost();
+
+ virtual void draw();
+
+ LLRect getLastSelectedItemRect();
+
+ LLRect getSelectedItemsRect();
private:
@@ -381,6 +402,10 @@ private:
LLRect mPrevNotifyParentRect;
LLTextBox* mNoItemsCommentTextbox;
+
+ LLViewBorder* mSelectedItemsBorder;
+
+ commit_signal_t mOnReturnSignal;
};
#endif
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index 4dc8872557..29f7cc1851 100644
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -536,8 +536,15 @@ BOOL LLPanelPeople::postBuild()
mNearbyList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mNearbyList));
mRecentList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mRecentList));
+ // Set openning IM as default on return action for avatar lists
+ mOnlineFriendList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
+ mAllFriendList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
+ mNearbyList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
+ mRecentList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
+
mGroupList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this));
mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this));
+ mGroupList->setReturnCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this));
LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>("tab_all");
accordion_tab->setDropDownStateChangedCallback(
diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp
index 13bd059d45..4ee9cba69c 100644
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -65,6 +65,8 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av
mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData"));
mAvatarList->setDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList));
mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2));
+ // Set onAvatarListDoubleClicked as default on_return action.
+ mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList));
mParticipantListMenu = new LLParticipantListMenu(*this);
mAvatarList->setContextMenu(mParticipantListMenu);
@@ -99,6 +101,7 @@ void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible)
void LLParticipantList::onAvatarListDoubleClicked(LLAvatarList* list)
{
+ // NOTE(EM): Should we check if there is multiple selection and start conference if it is so?
LLUUID clicked_id = list->getSelectedUUID();
if (clicked_id.isNull() || clicked_id == gAgent.getID())