diff options
author | Eugene Mutavchi <emutavchi@productengine.com> | 2009-11-20 20:27:53 +0200 |
---|---|---|
committer | Eugene Mutavchi <emutavchi@productengine.com> | 2009-11-20 20:27:53 +0200 |
commit | c9101f603a7969f4d46a7d8d911fa8805d926548 (patch) | |
tree | 5245dba3a5721dc1f3dd9a6b6cb979836ed75248 /indra/llui | |
parent | 0c468557b9e38ec4be201fa991499ed095a4d8c3 (diff) |
Implemented low task EXT-1153(FlatListView should support keyboard)
--HG--
branch : product-engine
Diffstat (limited to 'indra/llui')
-rw-r--r-- | indra/llui/llflatlistview.cpp | 241 | ||||
-rw-r--r-- | indra/llui/llflatlistview.h | 25 |
2 files changed, 266 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 |