diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index c592fccfb..cdac9b0a4 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -46,16 +46,36 @@ static LLRegisterWidget r("radio_group"); +/* + * A checkbox control with use_radio_style == true. + */ +class LLRadioCtrl : public LLCheckBoxCtrl +{ +public: + LLRadioCtrl(const std::string& name, const LLRect& rect, const std::string& label, const std::string& value = "", const LLFontGL* font = NULL, commit_callback_t commit_callback = NULL); + /*virtual*/ ~LLRadioCtrl(); + + virtual LLXMLNodePtr getXML(bool save_children = true) const; + /*virtual*/ void setValue(const LLSD& value); + + LLSD getPayload() { return mPayload; } +protected: + friend class LLUICtrlFactory; + + LLSD mPayload; // stores data that this item represents in the radio group +}; + LLRadioGroup::LLRadioGroup(const std::string& name, const LLRect& rect, S32 initial_index, commit_callback_t commit_callback, - BOOL border) : + bool border, bool allow_deselect) : LLUICtrl(name, rect, TRUE, commit_callback, FOLLOWS_LEFT | FOLLOWS_TOP), - mSelectedIndex(initial_index) + mSelectedIndex(initial_index), + mAllowDeselect(allow_deselect) { init(border); } -void LLRadioGroup::init(BOOL border) +void LLRadioGroup::init(bool border) { if (border) { @@ -67,12 +87,19 @@ void LLRadioGroup::init(BOOL border) } - - LLRadioGroup::~LLRadioGroup() { } +// virtual +BOOL LLRadioGroup::postBuild() +{ + if (!mRadioButtons.empty()) + { + mRadioButtons[0]->setTabStop(true); + } + return TRUE; +} // virtual void LLRadioGroup::setEnabled(BOOL enabled) @@ -133,16 +160,39 @@ void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled) BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) { - if (index < 0 || index >= (S32)mRadioButtons.size()) + if ((S32)mRadioButtons.size() <= index ) { return FALSE; } + if (mSelectedIndex >= 0) + { + LLRadioCtrl* old_radio_item = mRadioButtons[mSelectedIndex]; + old_radio_item->setTabStop(false); + old_radio_item->setValue( FALSE ); + } + else + { + mRadioButtons[0]->setTabStop(false); + } + mSelectedIndex = index; + if (mSelectedIndex >= 0) + { + LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; + radio_item->setTabStop(true); + radio_item->setValue( TRUE ); + + if (hasFocus()) + { + radio_item->focusFirstItem(FALSE, FALSE); + } + } + if (!from_event) { - setControlValue(getSelectedIndex()); + setControlValue(getValue()); } return TRUE; @@ -207,41 +257,23 @@ BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask) return handled; } -void LLRadioGroup::draw() +BOOL LLRadioGroup::handleMouseDown(S32 x, S32 y, MASK mask) { - S32 current_button = 0; - - BOOL take_focus = FALSE; - if (gFocusMgr.childHasKeyboardFocus(this)) + // grab focus preemptively, before child button takes mousecapture + // + if (hasTabStop()) { - take_focus = TRUE; + focusFirstItem(FALSE, FALSE); } - for (button_list_t::iterator iter = mRadioButtons.begin(); - iter != mRadioButtons.end(); ++iter) - { - LLRadioCtrl* radio = *iter; - BOOL selected = (current_button == mSelectedIndex); - radio->setValue( selected ); - if (take_focus && selected && !gFocusMgr.childHasKeyboardFocus(radio)) - { - // don't flash keyboard focus when navigating via keyboard - BOOL DONT_FLASH = FALSE; - radio->focusFirstItem(FALSE, DONT_FLASH); - } - current_button++; - } - - LLView::draw(); + return LLUICtrl::handleMouseDown(x, y, mask); } - // When adding a button, we need to ensure that the radio // group gets a message when the button is clicked. -LLRadioCtrl* LLRadioGroup::addRadioButton(const std::string& name, const std::string& label, const LLRect& rect, const LLFontGL* font ) +LLRadioCtrl* LLRadioGroup::addRadioButton(const std::string& name, const std::string& label, const LLRect& rect, const LLFontGL* font, const std::string& payload) { - // Highlight will get fixed in draw method above - LLRadioCtrl* radio = new LLRadioCtrl(name, rect, label, font, boost::bind(&LLRadioGroup::onClickButton, this, _1)); + LLRadioCtrl* radio = new LLRadioCtrl(name, rect, label, payload, font, boost::bind(&LLRadioGroup::onClickButton, this, _1)); addChild(radio); mRadioButtons.push_back(radio); return radio; @@ -252,7 +284,7 @@ LLRadioCtrl* LLRadioGroup::addRadioButton(const std::string& name, const std::st void LLRadioGroup::onClickButton(LLUICtrl* ctrl) { - // llinfos << "LLRadioGroup::onClickButton" << llendl; + // LL_INFOS() << "LLRadioGroup::onClickButton" << LL_ENDL; LLRadioCtrl* clicked_radio = dynamic_cast(ctrl); if (!clicked_radio) return; @@ -263,9 +295,15 @@ void LLRadioGroup::onClickButton(LLUICtrl* ctrl) LLRadioCtrl* radio = *iter; if (radio == clicked_radio) { - // llinfos << "clicked button " << counter << llendl; - setSelectedIndex(index); - setControlValue(index); + if (index == mSelectedIndex && mAllowDeselect) + { + // don't select anything + setSelectedIndex(-1); + } + else + { + setSelectedIndex(index); + } // BUG: Calls click callback even if button didn't actually change onCommit(); @@ -281,13 +319,12 @@ void LLRadioGroup::onClickButton(LLUICtrl* ctrl) void LLRadioGroup::setValue( const LLSD& value ) { - std::string value_name = value.asString(); int idx = 0; for (button_list_t::const_iterator iter = mRadioButtons.begin(); iter != mRadioButtons.end(); ++iter) { LLRadioCtrl* radio = *iter; - if (radio->getName() == value_name) + if (radio->getPayload().asString() == value.asString()) { setSelectedIndex(idx); idx = -1; @@ -304,8 +341,7 @@ void LLRadioGroup::setValue( const LLSD& value ) } else { - llwarns << "LLRadioGroup::setValue: radio_item with name=\"" << value_name << "\" not found, radio_group values are set by radio_item name not value. Falling back on LLUICtrl::setValue." << llendl; - LLUICtrl::setValue(value); + setSelectedIndex(-1, TRUE); } } } @@ -317,7 +353,7 @@ LLSD LLRadioGroup::getValue() const for (button_list_t::const_iterator iter = mRadioButtons.begin(); iter != mRadioButtons.end(); ++iter) { - if (idx == index) return LLSD((*iter)->getName()); + if (idx == index) return LLSD((*iter)->getPayload()); ++idx; } return LLSD(); @@ -333,6 +369,7 @@ LLXMLNodePtr LLRadioGroup::getXML(bool save_children) const // Attributes node->createChild("draw_border", TRUE)->setBoolValue(mHasBorder); + node->createChild("allow_deselect", TRUE)->setBoolValue(mAllowDeselect); // Contents @@ -355,17 +392,21 @@ LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory U32 initial_value = 0; node->getAttributeU32("initial_value", initial_value); - BOOL draw_border = TRUE; - node->getAttributeBOOL("draw_border", draw_border); + bool draw_border = true; + node->getAttribute_bool("draw_border", draw_border); + + bool allow_deselect = false; + node->getAttribute_bool("allow_deselect", allow_deselect); LLRect rect; createRect(node, rect, parent, LLRect()); - LLRadioGroup* radio_group = new LLRadioGroup("radio_group", + LLRadioGroup* radio_group = new LLRadioGroup("radio_group", rect, initial_value, NULL, - draw_border); + draw_border, + allow_deselect); const std::string& contents = node->getValue(); @@ -406,7 +447,13 @@ LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory createRect(child, item_rect, radio_group, rect); std::string item_label = child->getTextContents(); - LLRadioCtrl* radio = radio_group->addRadioButton("radio", item_label, item_rect, font); + node->getAttributeString("label", item_label); + std::string item_name("radio"); + node->getAttributeString("name", item_name); + std::string payload(item_name); // Support old-style name as payload + node->getAttributeString("value", payload); + node->getAttributeString("initial_value", payload); // Synonym + LLRadioCtrl* radio = radio_group->addRadioButton(item_name, item_label, item_rect, font, payload); radio->initFromXML(child, radio_group); } @@ -432,11 +479,10 @@ LLUUID LLRadioGroup::getCurrentID() const BOOL LLRadioGroup::setSelectedByValue(const LLSD& value, BOOL selected) { S32 idx = 0; - std::string value_string = value.asString(); for (button_list_t::const_iterator iter = mRadioButtons.begin(); iter != mRadioButtons.end(); ++iter) { - if((*iter)->getName() == value_string) + if((*iter)->getPayload().asString() == value.asString()) { setSelectedIndex(idx); return TRUE; @@ -455,11 +501,10 @@ LLSD LLRadioGroup::getSelectedValue() BOOL LLRadioGroup::isSelected(const LLSD& value) const { S32 idx = 0; - std::string value_string = value.asString(); for (button_list_t::const_iterator iter = mRadioButtons.begin(); iter != mRadioButtons.end(); ++iter) { - if((*iter)->getName() == value_string) + if((*iter)->getPayload().asString() == value.asString()) { if (idx == mSelectedIndex) { @@ -481,6 +526,17 @@ BOOL LLRadioGroup::operateOnAll(EOperation op) return FALSE; } +LLRadioCtrl::LLRadioCtrl(const std::string& name, const LLRect& rect, const std::string& label, const std::string& value, const LLFontGL* font, commit_callback_t commit_callback) +: LLCheckBoxCtrl(name, rect, label, font, commit_callback, FALSE, RADIO_STYLE), + mPayload(value) +{ + setTabStop(FALSE); + // use name as default "Value" for backwards compatibility + if (value.empty()) + { + mPayload = name; + } +} LLRadioCtrl::~LLRadioCtrl() { @@ -499,7 +555,7 @@ LLXMLNodePtr LLRadioCtrl::getXML(bool save_children) const node->setName(LL_RADIO_ITEM_TAG); - node->setStringValue(getLabel()); + node->createChild("value", TRUE)->setStringValue(mPayload); return node; } diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index 3cc042217..e21c7bc1f 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -37,26 +37,6 @@ #include "llcheckboxctrl.h" #include "llctrlselectioninterface.h" - -/* - * A checkbox control with use_radio_style == true. - */ -class LLRadioCtrl : public LLCheckBoxCtrl -{ -public: - LLRadioCtrl(const std::string& name, const LLRect& rect, const std::string& label, const LLFontGL* font = NULL, - commit_callback_t commit_callback = NULL) : - LLCheckBoxCtrl(name, rect, label, font, commit_callback, FALSE, RADIO_STYLE) - { - setTabStop(FALSE); - } - /*virtual*/ ~LLRadioCtrl(); - - virtual LLXMLNodePtr getXML(bool save_children = true) const; - /*virtual*/ void setValue(const LLSD& value); -}; - - /* * An invisible view containing multiple mutually exclusive toggling * buttons (usually radio buttons). Automatically handles the mutex @@ -66,25 +46,32 @@ class LLRadioGroup : public LLUICtrl, public LLCtrlSelectionInterface { public: - // Radio group constructor. Doesn't rely on - // needing a control + + // Radio group constructor. Doesn't rely on needing a control LLRadioGroup(const std::string& name, const LLRect& rect, S32 initial_index, commit_callback_t commit_callback, - BOOL border = TRUE); + bool border = true, bool allow_deselect = false); + +protected: + friend class LLUICtrlFactory; + +public: virtual ~LLRadioGroup(); + virtual BOOL postBuild(); + + virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual BOOL handleKeyHere(KEY key, MASK mask); virtual void setEnabled(BOOL enabled); virtual LLXMLNodePtr getXML(bool save_children = true) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); void setIndexEnabled(S32 index, BOOL enabled); - // return the index value of the selected item S32 getSelectedIndex() const { return mSelectedIndex; } - // set the index value programatically BOOL setSelectedIndex(S32 index, BOOL from_event = FALSE); @@ -92,14 +79,10 @@ public: virtual void setValue(const LLSD& value ); virtual LLSD getValue() const; - // Draw the group, but also fix the highlighting based on the control. - void draw(); - // You must use this method to add buttons to a radio group. // Don't use addChild -- it won't set the callback function // correctly. - LLRadioCtrl* addRadioButton(const std::string& name, const std::string& label, const LLRect& rect, const LLFontGL* font); - LLRadioCtrl* getRadioButton(const S32& index) { return mRadioButtons[index]; } + class LLRadioCtrl* addRadioButton(const std::string& name, const std::string& label, const LLRect& rect, const LLFontGL* font, const std::string& payload = ""); // Update the control as needed. Userdata must be a pointer to the button. void onClickButton(LLUICtrl* clicked_radio); @@ -123,14 +106,15 @@ public: private: // protected function shared by the two constructors. - void init(BOOL border); + void init(bool border); S32 mSelectedIndex; - typedef std::vector button_list_t; + + typedef std::vector button_list_t; button_list_t mRadioButtons; - BOOL mHasBorder; + bool mHasBorder; + bool mAllowDeselect; // user can click on an already selected option to deselect it }; - #endif diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 266beb7ce..9d7e2c0be 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -1787,7 +1787,7 @@ void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater, bool de child_list_t::const_iterator it, end=childs->end(); for (it=childs->begin(); it!=end; ++it) { - LLRadioCtrl *ctrl = dynamic_cast(*it); + LLView* ctrl = *it; if (ctrl && (ctrl->getName() == "texture")) { ctrl->setLabelArg("[UPLOADFEE]", fee); diff --git a/indra/newview/llfloatertest.cpp b/indra/newview/llfloatertest.cpp index 22628dc09..46d0978e7 100644 --- a/indra/newview/llfloatertest.cpp +++ b/indra/newview/llfloatertest.cpp @@ -81,8 +81,6 @@ private: LLIconCtrl* mIcon; LLLineEditor* mLineEditor; LLRadioGroup* mRadioGroup; - LLRadioCtrl* mRadio1; - LLRadioCtrl* mRadio2; LLScrollContainer* mScroll; LLScrollListCtrl* mScrollList; LLTabContainer* mTab;