diff options
Diffstat (limited to 'indra/llui/llcombobox.cpp')
-rw-r--r-- | indra/llui/llcombobox.cpp | 841 |
1 files changed, 346 insertions, 495 deletions
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 538641d060..910bab9a97 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -2,30 +2,25 @@ * @file llcombobox.cpp * @brief LLComboBox base class * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -47,179 +42,161 @@ #include "llwindow.h" #include "llfloater.h" #include "llscrollbar.h" +#include "llscrolllistcell.h" +#include "llscrolllistitem.h" #include "llcontrol.h" #include "llfocusmgr.h" #include "lllineeditor.h" #include "v2math.h" +#include "lluictrlfactory.h" +#include "lltooltip.h" // Globals S32 LLCOMBOBOX_HEIGHT = 0; S32 LLCOMBOBOX_WIDTH = 0; S32 MAX_COMBO_WIDTH = 500; -static LLRegisterWidget<LLComboBox> r1("combo_box"); +static LLDefaultChildRegistry::Register<LLComboBox> register_combo_box("combo_box"); -LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString& label, - void (*commit_callback)(LLUICtrl*,void*), - void *callback_userdata - ) -: LLUICtrl(name, rect, TRUE, commit_callback, callback_userdata, - FOLLOWS_LEFT | FOLLOWS_TOP), - mTextEntry(NULL), - mArrowImage(NULL), - mAllowTextEntry(FALSE), - mMaxChars(20), - mTextEntryTentative(TRUE), - mListPosition(BELOW), - mPrearrangeCallback( NULL ), - mTextEntryCallback( NULL ) +void LLComboBox::PreferredPositionValues::declareValues() { - // Always use text box - // Text label button - mButton = new LLButton(label, - LLRect(), - LLString::null, - NULL, this); - mButton->setImageUnselected("square_btn_32x128.tga"); - mButton->setImageSelected("square_btn_selected_32x128.tga"); - mButton->setImageDisabled("square_btn_32x128.tga"); - mButton->setImageDisabledSelected("square_btn_selected_32x128.tga"); - mButton->setScaleImage(TRUE); - - mButton->setMouseDownCallback(onButtonDown); - mButton->setFont(LLFontGL::sSansSerifSmall); - mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT); - mButton->setHAlign( LLFontGL::LEFT ); - mButton->setRightHPad(2); - addChild(mButton); - - // disallow multiple selection - mList = new LLScrollListCtrl( - "ComboBox", LLRect(), - &LLComboBox::onItemSelected, this, FALSE); - mList->setVisible(FALSE); - mList->setBgWriteableColor( LLColor4(1,1,1,1) ); - mList->setCommitOnKeyboardMovement(FALSE); - addChild(mList); - - mArrowImage = LLUI::sImageProvider->getUIImage("combobox_arrow.tga"); - mButton->setImageOverlay("combobox_arrow.tga", LLFontGL::RIGHT); + declare("above", ABOVE); + declare("below", BELOW); +} - updateLayout(); +LLComboBox::ItemParams::ItemParams() +: label("label") +{ } -LLComboBox::~LLComboBox() +LLComboBox::Params::Params() +: allow_text_entry("allow_text_entry", false), + allow_new_values("allow_new_values", false), + show_text_as_tentative("show_text_as_tentative", true), + max_chars("max_chars", 20), + list_position("list_position", BELOW), + items("item"), + combo_button("combo_button"), + combo_list("combo_list"), + combo_editor("combo_editor"), + drop_down_button("drop_down_button") { - // children automatically deleted, including mMenu, mButton + addSynonym(items, "combo_item"); } -// virtual -LLXMLNodePtr LLComboBox::getXML(bool save_children) const + +LLComboBox::LLComboBox(const LLComboBox::Params& p) +: LLUICtrl(p), + mTextEntry(NULL), + mTextEntryTentative(p.show_text_as_tentative), + mHasAutocompletedText(false), + mAllowTextEntry(p.allow_text_entry), + mAllowNewValues(p.allow_new_values), + mMaxChars(p.max_chars), + mPrearrangeCallback(p.prearrange_callback()), + mTextEntryCallback(p.text_entry_callback()), + mListPosition(p.list_position), + mLastSelectedIndex(-1), + mLabel(p.label) { - LLXMLNodePtr node = LLUICtrl::getXML(); + // Text label button - // Attributes + LLButton::Params button_params = (mAllowTextEntry ? p.combo_button : p.drop_down_button); + button_params.mouse_down_callback.function( + boost::bind(&LLComboBox::onButtonMouseDown, this)); + button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT); + button_params.rect(p.rect); - node->createChild("allow_text_entry", TRUE)->setBoolValue(mAllowTextEntry); + if(mAllowTextEntry) + { + button_params.pad_right(2); + } - node->createChild("max_chars", TRUE)->setIntValue(mMaxChars); + mArrowImage = button_params.image_unselected; - // Contents + mButton = LLUICtrlFactory::create<LLButton>(button_params); - std::vector<LLScrollListItem*> data_list = mList->getAllData(); - std::vector<LLScrollListItem*>::iterator data_itor; - for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor) + + if(mAllowTextEntry) { - LLScrollListItem* item = *data_itor; - LLScrollListCell* cell = item->getColumn(0); - if (cell) - { - LLXMLNodePtr item_node = node->createChild("combo_item", FALSE); - LLSD value = item->getValue(); - item_node->createChild("value", TRUE)->setStringValue(value.asString()); - item_node->createChild("enabled", TRUE)->setBoolValue(item->getEnabled()); - item_node->setStringValue(cell->getValue().asString()); - } + //redo to compensate for button hack that leaves space for a character + //unless it is a "minimal combobox"(drop down) + mButton->setRightHPad(2); } + addChild(mButton); - return node; -} - -// static -LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) -{ - LLString name("combo_box"); - node->getAttributeString("name", name); - - LLString label(""); - node->getAttributeString("label", label); + LLScrollListCtrl::Params params = p.combo_list; + params.name("ComboBox"); + params.commit_callback.function(boost::bind(&LLComboBox::onItemSelected, this, _2)); + params.visible(false); + params.commit_on_keyboard_movement(false); - LLRect rect; - createRect(node, rect, parent, LLRect()); + mList = LLUICtrlFactory::create<LLScrollListCtrl>(params); + addChild(mList); - BOOL allow_text_entry = FALSE; - node->getAttributeBOOL("allow_text_entry", allow_text_entry); + // Mouse-down on button will transfer mouse focus to the list + // Grab the mouse-up event and make sure the button state is correct + mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this)); - S32 max_chars = 20; - node->getAttributeS32("max_chars", max_chars); + for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items().begin(); + it != p.items().end(); + ++it) + { + LLScrollListItem::Params item_params = *it; + if (it->label.isProvided()) + { + item_params.columns.add().value(it->label()); + } - LLUICtrlCallback callback = NULL; + mList->addRow(item_params); + } - LLComboBox* combo_box = new LLComboBox(name, - rect, - label, - callback, - NULL); - combo_box->setAllowTextEntry(allow_text_entry, max_chars); + createLineEditor(p); - combo_box->initFromXML(node, parent); + mTopLostSignalConnection = setTopLostCallback(boost::bind(&LLComboBox::hideList, this)); +} - const LLString& contents = node->getValue(); +void LLComboBox::initFromParams(const LLComboBox::Params& p) +{ + LLUICtrl::initFromParams(p); - if (contents.find_first_not_of(" \n\t") != contents.npos) + if (!acceptsTextInput() && mLabel.empty()) { - llerrs << "Legacy combo box item format used! Please convert to <combo_item> tags!" << llendl; + selectFirstItem(); } - else - { - LLXMLNodePtr child; - for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) - { - if (child->hasName("combo_item")) - { - LLString label = child->getTextContents(); - - LLString value = label; - child->getAttributeString("value", value); +} - combo_box->add(label, LLSD(value) ); - } - } +// virtual +BOOL LLComboBox::postBuild() +{ + if (mControlVariable) + { + setValue(mControlVariable->getValue()); // selects the appropriate item } - - combo_box->selectFirstItem(); - - return combo_box; + return TRUE; } -void LLComboBox::setEnabled(BOOL enabled) + +LLComboBox::~LLComboBox() { - LLView::setEnabled(enabled); - mButton->setEnabled(enabled); + // children automatically deleted, including mMenu, mButton + + // explicitly disconect this signal, since base class destructor might fire top lost + mTopLostSignalConnection.disconnect(); } + void LLComboBox::clear() { if (mTextEntry) { - mTextEntry->setText(LLString::null); + mTextEntry->setText(LLStringUtil::null); } - mButton->setLabelSelected(LLString::null); - mButton->setLabelUnselected(LLString::null); - mButton->setDisabledLabel(LLString::null); - mButton->setDisabledSelectedLabel(LLString::null); + mButton->setLabelSelected(LLStringUtil::null); + mButton->setLabelUnselected(LLStringUtil::null); mList->deselectAllItems(); + mLastSelectedIndex = -1; } void LLComboBox::onCommit() @@ -231,6 +208,7 @@ void LLComboBox::onCommit() mTextEntry->setValue(getSimple()); mTextEntry->setTentative(FALSE); } + setControlValue(getValue()); LLUICtrl::onCommit(); } @@ -256,39 +234,51 @@ void LLComboBox::resetDirty() // add item "name" to menu -LLScrollListItem* LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled) +LLScrollListItem* LLComboBox::add(const std::string& name, EAddPosition pos, BOOL enabled) { LLScrollListItem* item = mList->addSimpleElement(name, pos); item->setEnabled(enabled); - mList->selectFirstItem(); + if (!mAllowTextEntry && mLabel.empty()) + { + selectFirstItem(); + } return item; } // add item "name" with a unique id to menu -LLScrollListItem* LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAddPosition pos, BOOL enabled ) { LLScrollListItem* item = mList->addSimpleElement(name, pos, id); item->setEnabled(enabled); - mList->selectFirstItem(); + if (!mAllowTextEntry && mLabel.empty()) + { + selectFirstItem(); + } return item; } // add item "name" with attached userdata -LLScrollListItem* LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddPosition pos, BOOL enabled ) { LLScrollListItem* item = mList->addSimpleElement(name, pos); item->setEnabled(enabled); item->setUserdata( userdata ); - mList->selectFirstItem(); + if (!mAllowTextEntry && mLabel.empty()) + { + selectFirstItem(); + } return item; } // add item "name" with attached generic data -LLScrollListItem* LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled ) +LLScrollListItem* LLComboBox::add(const std::string& name, LLSD value, EAddPosition pos, BOOL enabled ) { LLScrollListItem* item = mList->addSimpleElement(name, pos, value); item->setEnabled(enabled); - mList->selectFirstItem(); + if (!mAllowTextEntry && mLabel.empty()) + { + selectFirstItem(); + } return item; } @@ -297,9 +287,9 @@ LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos) return mList->addSeparator(pos); } -void LLComboBox::sortByName() +void LLComboBox::sortByName(BOOL ascending) { - mList->sortByColumn(0, TRUE); + mList->sortOnce(0, ascending); } @@ -312,6 +302,7 @@ BOOL LLComboBox::setSimple(const LLStringExplicit& name) if (found) { setLabel(name); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; @@ -326,14 +317,19 @@ void LLComboBox::setValue(const LLSD& value) LLScrollListItem* item = mList->getFirstSelected(); if (item) { - setLabel( mList->getSelectedItemLabel() ); + setLabel(getSelectedItemLabel()); } + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else + { + mLastSelectedIndex = -1; } } -const LLString LLComboBox::getSimple() const +const std::string LLComboBox::getSimple() const { - const LLString res = mList->getSelectedItemLabel(); + const std::string res = getSelectedItemLabel(); if (res.empty() && mAllowTextEntry) { return mTextEntry->getText(); @@ -344,7 +340,7 @@ const LLString LLComboBox::getSimple() const } } -const LLString LLComboBox::getSelectedItemLabel(S32 column) const +const std::string LLComboBox::getSelectedItemLabel(S32 column) const { return mList->getSelectedItemLabel(column); } @@ -375,6 +371,7 @@ void LLComboBox::setLabel(const LLStringExplicit& name) if (mList->selectItemByLabel(name, FALSE)) { mTextEntry->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } else { @@ -384,15 +381,12 @@ void LLComboBox::setLabel(const LLStringExplicit& name) if (!mAllowTextEntry) { - mButton->setLabelUnselected(name); - mButton->setLabelSelected(name); - mButton->setDisabledLabel(name); - mButton->setDisabledSelectedLabel(name); + mButton->setLabel(name); } } -BOOL LLComboBox::remove(const LLString& name) +BOOL LLComboBox::remove(const std::string& name) { BOOL found = mList->selectItemByLabel(name); @@ -403,6 +397,7 @@ BOOL LLComboBox::remove(const LLString& name) { mList->deleteSingleItem(mList->getItemIndex(item)); } + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; @@ -413,6 +408,7 @@ BOOL LLComboBox::remove(S32 index) if (index < mList->getItemCount()) { mList->deleteSingleItem(index); + setLabel(getSelectedItemLabel()); return TRUE; } return FALSE; @@ -430,41 +426,31 @@ void LLComboBox::onFocusLost() LLUICtrl::onFocusLost(); } -void LLComboBox::onLostTop() -{ - hideList(); -} - - void LLComboBox::setButtonVisible(BOOL visible) { + static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0); + mButton->setVisible(visible); if (mTextEntry) { LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); if (visible) { - text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); + S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; + text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button; } //mTextEntry->setRect(text_entry_rect); mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE); } } -void LLComboBox::draw() -{ - mButton->setEnabled(getEnabled() /*&& !mList->isEmpty()*/); - - // Draw children normally - LLUICtrl::draw(); -} - BOOL LLComboBox::setCurrentByIndex( S32 index ) { BOOL found = mList->selectNthItem( index ); if (found) { - setLabel(mList->getSelectedItemLabel()); + setLabel(getSelectedItemLabel()); + mLastSelectedIndex = index; } return found; } @@ -480,55 +466,49 @@ S32 LLComboBox::getCurrentIndex() const } -void LLComboBox::updateLayout() +void LLComboBox::createLineEditor(const LLComboBox::Params& p) { + static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0); LLRect rect = getLocalRect(); if (mAllowTextEntry) { - S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton"); - mButton->setRect(LLRect( getRect().getWidth() - llmax(8,mArrowImage->getWidth()) - 2 * shadow_size, + S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0; + S32 shadow_size = drop_shadow_button; + mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size, rect.mTop, rect.mRight, rect.mBottom)); mButton->setTabStop(FALSE); + mButton->setHAlign(LLFontGL::HCENTER); - if (!mTextEntry) - { - LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); - text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton"); - // clear label on button - LLString cur_label = mButton->getLabelSelected(); - mTextEntry = new LLLineEditor("combo_text_entry", - text_entry_rect, - "", - LLFontGL::sSansSerifSmall, - mMaxChars, - onTextCommit, - onTextEntry, - NULL, - this); - mTextEntry->setSelectAllonFocusReceived(TRUE); - mTextEntry->setHandleEditKeysDirectly(TRUE); - mTextEntry->setCommitOnFocusLost(FALSE); - mTextEntry->setText(cur_label); - mTextEntry->setIgnoreTab(TRUE); - mTextEntry->setFollowsAll(); - addChild(mTextEntry); - } - else - { - mTextEntry->setVisible(TRUE); - mTextEntry->setMaxTextLength(mMaxChars); - } + LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button; + // clear label on button + std::string cur_label = mButton->getLabelSelected(); + LLLineEditor::Params params = p.combo_editor; + params.rect(text_entry_rect); + params.default_text(LLStringUtil::null); + params.max_length_bytes(mMaxChars); + params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2)); + params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1)); + params.commit_on_focus_lost(false); + params.follows.flags(FOLLOWS_ALL); + params.label(mLabel); + mTextEntry = LLUICtrlFactory::create<LLLineEditor> (params); + mTextEntry->setText(cur_label); + mTextEntry->setIgnoreTab(TRUE); + addChild(mTextEntry); // clear label on button - setLabel(LLString::null); + setLabel(LLStringUtil::null); mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT); } - else if (!mAllowTextEntry) + else { mButton->setRect(rect); mButton->setTabStop(TRUE); - + mButton->setHAlign(LLFontGL::LEFT); + mButton->setLabel(mLabel.getString()); + if (mTextEntry) { mTextEntry->setVisible(FALSE); @@ -632,140 +612,131 @@ void LLComboBox::showList() mList->setFocus(TRUE); - // register ourselves as a "top" control - // effectively putting us into a special draw layer - // and not affecting the bounding rectangle calculation - gFocusMgr.setTopCtrl(this); - // Show the list and push the button down mButton->setToggleState(TRUE); mList->setVisible(TRUE); + LLUI::addPopup(this); + setUseBoundingRect(TRUE); +// updateBoundingRect(); } void LLComboBox::hideList() { - //*HACK: store the original value explicitly somewhere, not just in label - LLString orig_selection = mAllowTextEntry ? mTextEntry->getText() : mButton->getLabelSelected(); - - // assert selection in list - mList->selectItemByLabel(orig_selection, FALSE); + if (mList->getVisible()) + { + // assert selection in list + if(mAllowNewValues) + { + // mLastSelectedIndex = -1 means that we entered a new value, don't select + // any of existing items in this case. + if(mLastSelectedIndex >= 0) + mList->selectNthItem(mLastSelectedIndex); + } + else if(mLastSelectedIndex >= 0) + mList->selectNthItem(mLastSelectedIndex); - mButton->setToggleState(FALSE); - mList->setVisible(FALSE); - mList->highlightNthItem(-1); + mButton->setToggleState(FALSE); + mList->setVisible(FALSE); + mList->mouseOverHighlightNthItem(-1); - setUseBoundingRect(FALSE); - if( gFocusMgr.getTopCtrl() == this ) - { - gFocusMgr.setTopCtrl(NULL); + setUseBoundingRect(FALSE); + LLUI::removePopup(this); +// updateBoundingRect(); } } -//------------------------------------------------------------------ -// static functions -//------------------------------------------------------------------ - -// static -void LLComboBox::onButtonDown(void *userdata) +void LLComboBox::onButtonMouseDown() { - LLComboBox *self = (LLComboBox *)userdata; - - if (!self->mList->getVisible()) + if (!mList->getVisible()) { - LLScrollListItem* last_selected_item = self->mList->getLastSelectedItem(); - if (last_selected_item) - { - // highlight the original selection before potentially selecting a new item - self->mList->highlightNthItem(self->mList->getItemIndex(last_selected_item)); - } + // this might change selection, so do it first + prearrangeList(); - if( self->mPrearrangeCallback ) + // highlight the last selected item from the original selection before potentially selecting a new item + // as visual cue to original value of combo box + LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); + if (last_selected_item) { - self->mPrearrangeCallback( self, self->mCallbackUserData ); + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); } - if (self->mList->getItemCount() != 0) + if (mList->getItemCount() != 0) { - self->showList(); + showList(); } - self->setFocus( TRUE ); + setFocus( TRUE ); // pass mouse capture on to list if button is depressed - if (self->mButton->hasMouseCapture()) + if (mButton->hasMouseCapture()) { - gFocusMgr.setMouseCapture(self->mList); + gFocusMgr.setMouseCapture(mList); + + // But keep the "pressed" look, which buttons normally lose when they + // lose focus + mButton->setForcePressedState(true); } } else { - self->hideList(); + hideList(); } } -// static -void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata) +void LLComboBox::onListMouseUp() { - // Note: item is the LLScrollListCtrl - LLComboBox *self = (LLComboBox *) userdata; + // In some cases this is the termination of a mouse click that started on + // the button, so clear its pressed state + mButton->setForcePressedState(false); +} - const LLString name = self->mList->getSelectedItemLabel(); +//------------------------------------------------------------------ +// static functions +//------------------------------------------------------------------ - S32 cur_id = self->getCurrentIndex(); - if (cur_id != -1) +void LLComboBox::onItemSelected(const LLSD& data) +{ + mLastSelectedIndex = getCurrentIndex(); + if (mLastSelectedIndex != -1) { - self->setLabel(name); + setLabel(getSelectedItemLabel()); - if (self->mAllowTextEntry) + if (mAllowTextEntry) { - gFocusMgr.setKeyboardFocus(self->mTextEntry); - self->mTextEntry->selectAll(); + gFocusMgr.setKeyboardFocus(mTextEntry); + mTextEntry->selectAll(); } } - // hiding the list reasserts the old value stored in the text editor/dropdown button - self->hideList(); + hideList(); // commit does the reverse, asserting the value in the list - self->onCommit(); + onCommit(); } -BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) +BOOL LLComboBox::handleToolTip(S32 x, S32 y, MASK mask) { - LLString tool_tip; + std::string tool_tip; - if(LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen)) + if(LLUICtrl::handleToolTip(x, y, mask)) { return TRUE; } - if (LLUI::sShowXUINames) - { - tool_tip = getShowNamesToolTip(); - } - else + tool_tip = getToolTip(); + if (tool_tip.empty()) { - tool_tip = getToolTip(); - if (tool_tip.empty()) - { - tool_tip = getSelectedItemLabel(); - } + tool_tip = getSelectedItemLabel(); } if( !tool_tip.empty() ) { - msg = tool_tip; - - // Convert rect local to screen coordinates - localPointToScreen( - 0, 0, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - localPointToScreen( - getRect().getWidth(), getRect().getHeight(), - &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(tool_tip) + .sticky_rect(calcScreenRect())); } return TRUE; } @@ -775,16 +746,32 @@ BOOL LLComboBox::handleKeyHere(KEY key, MASK mask) BOOL result = FALSE; if (hasFocus()) { + if (mList->getVisible() + && key == KEY_ESCAPE && mask == MASK_NONE) + { + hideList(); + return TRUE; + } //give list a chance to pop up and handle key LLScrollListItem* last_selected_item = mList->getLastSelectedItem(); if (last_selected_item) { // highlight the original selection before potentially selecting a new item - mList->highlightNthItem(mList->getItemIndex(last_selected_item)); + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); } result = mList->handleKeyHere(key, mask); + + // will only see return key if it is originating from line editor + // since the dropdown button eats the key + if (key == KEY_RETURN) + { + // don't show list and don't eat key input when committing + // free-form text entry with RETURN since user already knows + // what they are trying to select + return FALSE; + } // if selection has changed, pop open list - if (mList->getLastSelectedItem() != last_selected_item) + else if (mList->getLastSelectedItem() != last_selected_item) { showList(); } @@ -804,7 +791,7 @@ BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char) if (last_selected_item) { // highlight the original selection before potentially selecting a new item - mList->highlightNthItem(mList->getItemIndex(last_selected_item)); + mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item)); } result = mList->handleUnicodeCharHere(uni_char); if (mList->getLastSelectedItem() != last_selected_item) @@ -816,46 +803,37 @@ BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char) return result; } -void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative) -{ - mAllowTextEntry = allow; - mTextEntryTentative = set_tentative; - mMaxChars = max_chars; - - updateLayout(); -} - void LLComboBox::setTextEntry(const LLStringExplicit& text) { if (mTextEntry) { mTextEntry->setText(text); + mHasAutocompletedText = FALSE; updateSelection(); } } -//static -void LLComboBox::onTextEntry(LLLineEditor* line_editor, void* user_data) +void LLComboBox::onTextEntry(LLLineEditor* line_editor) { - LLComboBox* self = (LLComboBox*)user_data; - - if (self->mTextEntryCallback) + if (mTextEntryCallback != NULL) { - (*self->mTextEntryCallback)(line_editor, self->mCallbackUserData); + (mTextEntryCallback)(line_editor, LLSD()); } KEY key = gKeyboard->currentKey(); if (key == KEY_BACKSPACE || key == KEY_DELETE) { - if (self->mList->selectItemByLabel(line_editor->getText(), FALSE)) + if (mList->selectItemByLabel(line_editor->getText(), FALSE)) { line_editor->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } else { - line_editor->setTentative(self->mTextEntryTentative); - self->mList->deselectAllItems(); + line_editor->setTentative(mTextEntryTentative); + mList->deselectAllItems(); + mLastSelectedIndex = -1; } return; } @@ -868,17 +846,14 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor, void* user_data) if (key == KEY_DOWN) { - self->setCurrentByIndex(llmin(self->getItemCount() - 1, self->getCurrentIndex() + 1)); - if (!self->mList->getVisible()) + setCurrentByIndex(llmin(getItemCount() - 1, getCurrentIndex() + 1)); + if (!mList->getVisible()) { - if( self->mPrearrangeCallback ) - { - self->mPrearrangeCallback( self, self->mCallbackUserData ); - } + prearrangeList(); - if (self->mList->getItemCount() != 0) + if (mList->getItemCount() != 0) { - self->showList(); + showList(); } } line_editor->selectAll(); @@ -886,17 +861,14 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor, void* user_data) } else if (key == KEY_UP) { - self->setCurrentByIndex(llmax(0, self->getCurrentIndex() - 1)); - if (!self->mList->getVisible()) + setCurrentByIndex(llmax(0, getCurrentIndex() - 1)); + if (!mList->getVisible()) { - if( self->mPrearrangeCallback ) - { - self->mPrearrangeCallback( self, self->mCallbackUserData ); - } + prearrangeList(); - if (self->mList->getItemCount() != 0) + if (mList->getItemCount() != 0) { - self->showList(); + showList(); } } line_editor->selectAll(); @@ -905,7 +877,7 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor, void* user_data) else { // RN: presumably text entry - self->updateSelection(); + updateSelection(); } } @@ -914,49 +886,49 @@ void LLComboBox::updateSelection() LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor()); // user-entered portion of string, based on assumption that any selected // text was a result of auto-completion - LLWString user_wstring = mTextEntry->hasSelection() ? left_wstring : mTextEntry->getWText(); - LLString full_string = mTextEntry->getText(); + LLWString user_wstring = mHasAutocompletedText ? left_wstring : mTextEntry->getWText(); + std::string full_string = mTextEntry->getText(); // go ahead and arrange drop down list on first typed character, even // though we aren't showing it... some code relies on prearrange // callback to populate content if( mTextEntry->getWText().size() == 1 ) { - if (mPrearrangeCallback) - { - mPrearrangeCallback( this, mCallbackUserData ); - } + prearrangeList(mTextEntry->getText()); } if (mList->selectItemByLabel(full_string, FALSE)) { mTextEntry->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } - else if (!mList->selectItemByPrefix(left_wstring, FALSE)) - { - mList->deselectAllItems(); - mTextEntry->setText(wstring_to_utf8str(user_wstring)); - mTextEntry->setTentative(mTextEntryTentative); - } - else + else if (mList->selectItemByPrefix(left_wstring, FALSE)) { - LLWString selected_item = utf8str_to_wstring(mList->getSelectedItemLabel()); + LLWString selected_item = utf8str_to_wstring(getSelectedItemLabel()); LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size()); mTextEntry->setText(wstring_to_utf8str(wtext)); mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size()); mTextEntry->endSelection(); mTextEntry->setTentative(FALSE); + mHasAutocompletedText = TRUE; + mLastSelectedIndex = mList->getFirstSelectedIndex(); + } + else // no matching items found + { + mList->deselectAllItems(); + mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion + mTextEntry->setTentative(mTextEntryTentative); + mHasAutocompletedText = FALSE; + mLastSelectedIndex = -1; } } -//static -void LLComboBox::onTextCommit(LLUICtrl* caller, void* user_data) +void LLComboBox::onTextCommit(const LLSD& data) { - LLComboBox* self = (LLComboBox*)user_data; - LLString text = self->mTextEntry->getText(); - self->setSimple(text); - self->onCommit(); - self->mTextEntry->selectAll(); + std::string text = mTextEntry->getText(); + setSimple(text); + onCommit(); + mTextEntry->selectAll(); } void LLComboBox::setFocus(BOOL b) @@ -973,6 +945,14 @@ void LLComboBox::setFocus(BOOL b) } } +void LLComboBox::prearrangeList(std::string filter) +{ + if (mPrearrangeCallback) + { + mPrearrangeCallback(this, LLSD(filter)); + } +} + //============================================================================ // LLCtrlListInterface functions @@ -992,7 +972,7 @@ void LLComboBox::clearColumns() mList->clearColumns(); } -void LLComboBox::setColumnLabel(const LLString& column, const LLString& label) +void LLComboBox::setColumnLabel(const std::string& column, const std::string& label) { mList->setColumnLabel(column, label); } @@ -1002,7 +982,7 @@ LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, vo return mList->addElement(value, pos, userdata); } -LLScrollListItem* LLComboBox::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id) +LLScrollListItem* LLComboBox::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) { return mList->addSimpleElement(value, pos, id); } @@ -1012,8 +992,9 @@ void LLComboBox::clearRows() mList->clearRows(); } -void LLComboBox::sortByColumn(LLString name, BOOL ascending) +void LLComboBox::sortByColumn(const std::string& name, BOOL ascending) { + mList->sortByColumn(name, ascending); } //============================================================================ @@ -1025,7 +1006,8 @@ BOOL LLComboBox::setCurrentByID(const LLUUID& id) if (found) { - setLabel(mList->getSelectedItemLabel()); + setLabel(getSelectedItemLabel()); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; @@ -1040,7 +1022,7 @@ BOOL LLComboBox::setSelectedByValue(const LLSD& value, BOOL selected) BOOL found = mList->setSelectedByValue(value, selected); if (found) { - setLabel(mList->getSelectedItemLabel()); + setLabel(getSelectedItemLabel()); } return found; } @@ -1081,153 +1063,22 @@ BOOL LLComboBox::selectItemRange( S32 first, S32 last ) } -// -// LLFlyoutButton -// - -static LLRegisterWidget<LLFlyoutButton> r2("flyout_button"); +static LLDefaultChildRegistry::Register<LLIconsComboBox> register_icons_combo_box("icons_combo_box"); -const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24; +LLIconsComboBox::Params::Params() +: icon_column("icon_column", ICON_COLUMN), + label_column("label_column", LABEL_COLUMN) +{} -LLFlyoutButton::LLFlyoutButton( - const LLString& name, - const LLRect &rect, - const LLString& label, - void (*commit_callback)(LLUICtrl*, void*) , - void *callback_userdata) -: LLComboBox(name, rect, LLString::null, commit_callback, callback_userdata), - mToggleState(FALSE), - mActionButton(NULL) -{ - // Always use text box - // Text label button - mActionButton = new LLButton(label, - LLRect(), LLString::null, NULL, this); - mActionButton->setScaleImage(TRUE); - - mActionButton->setClickedCallback(onActionButtonClick); - mActionButton->setFollowsAll(); - mActionButton->setHAlign( LLFontGL::HCENTER ); - mActionButton->setLabel(label); - addChild(mActionButton); - - mActionButtonImage = LLUI::getUIImage("flyout_btn_left.tga"); - mExpanderButtonImage = LLUI::getUIImage("flyout_btn_right.tga"); - mActionButtonImageSelected = LLUI::getUIImage("flyout_btn_left_selected.tga"); - mExpanderButtonImageSelected = LLUI::getUIImage("flyout_btn_right_selected.tga"); - mActionButtonImageDisabled = LLUI::getUIImage("flyout_btn_left_disabled.tga"); - mExpanderButtonImageDisabled = LLUI::getUIImage("flyout_btn_right_disabled.tga"); - - mActionButton->setImageSelected(mActionButtonImageSelected); - mActionButton->setImageUnselected(mActionButtonImage); - mActionButton->setImageDisabled(mActionButtonImageDisabled); - mActionButton->setImageDisabledSelected(LLPointer<LLUIImage>(NULL)); - - mButton->setImageSelected(mExpanderButtonImageSelected); - mButton->setImageUnselected(mExpanderButtonImage); - mButton->setImageDisabled(mExpanderButtonImageDisabled); - mButton->setImageDisabledSelected(LLPointer<LLUIImage>(NULL)); - mButton->setRightHPad(6); - - updateLayout(); -} +LLIconsComboBox::LLIconsComboBox(const LLIconsComboBox::Params& p) +: LLComboBox(p), + mIconColumnIndex(p.icon_column), + mLabelColumnIndex(p.label_column) +{} -//static -LLView* LLFlyoutButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +const std::string LLIconsComboBox::getSelectedItemLabel(S32 column) const { - LLString name = "flyout_button"; - node->getAttributeString("name", name); - - LLString label(""); - node->getAttributeString("label", label); - - LLRect rect; - createRect(node, rect, parent, LLRect()); - - LLUICtrlCallback callback = NULL; - - LLFlyoutButton* flyout_button = new LLFlyoutButton(name, - rect, - label, - callback, - NULL); + mButton->setImageOverlay(LLComboBox::getSelectedItemLabel(mIconColumnIndex), mButton->getImageOverlayHAlign()); - LLString list_position; - node->getAttributeString("list_position", list_position); - if (list_position == "below") - { - flyout_button->mListPosition = BELOW; - } - else if (list_position == "above") - { - flyout_button->mListPosition = ABOVE; - } - - - flyout_button->initFromXML(node, parent); - - LLXMLNodePtr child; - for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) - { - if (child->hasName("flyout_button_item")) - { - LLString label = child->getTextContents(); - - LLString value = label; - child->getAttributeString("value", value); - - flyout_button->add(label, LLSD(value) ); - } - } - - flyout_button->updateLayout(); - - return flyout_button; -} - -void LLFlyoutButton::updateLayout() -{ - LLComboBox::updateLayout(); - - mButton->setOrigin(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, 0); - mButton->reshape(FLYOUT_BUTTON_ARROW_WIDTH, getRect().getHeight()); - mButton->setFollows(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); - mButton->setTabStop(FALSE); - mButton->setImageOverlay(mListPosition == BELOW ? "down_arrow.tga" : "up_arrow.tga", LLFontGL::RIGHT); - - mActionButton->setOrigin(0, 0); - mActionButton->reshape(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, getRect().getHeight()); -} - -//static -void LLFlyoutButton::onActionButtonClick(void *user_data) -{ - LLFlyoutButton* buttonp = (LLFlyoutButton*)user_data; - // remember last list selection? - buttonp->mList->deselect(); - buttonp->onCommit(); + return LLComboBox::getSelectedItemLabel(mLabelColumnIndex); } - -void LLFlyoutButton::draw() -{ - mActionButton->setToggleState(mToggleState); - mButton->setToggleState(mToggleState); - - //FIXME: this should be an attribute of comboboxes, whether they have a distinct label or - // the label reflects the last selected item, for now we have to manually remove the label - mButton->setLabel(LLString::null); - LLComboBox::draw(); -} - -void LLFlyoutButton::setEnabled(BOOL enabled) -{ - mActionButton->setEnabled(enabled); - LLComboBox::setEnabled(enabled); -} - - -void LLFlyoutButton::setToggleState(BOOL state) -{ - mToggleState = state; -} - |