summaryrefslogtreecommitdiff
path: root/indra/llui/llaccordionctrl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/llaccordionctrl.cpp')
-rw-r--r--indra/llui/llaccordionctrl.cpp420
1 files changed, 378 insertions, 42 deletions
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp
index b5e870228a..f9ffaaa646 100644
--- a/indra/llui/llaccordionctrl.cpp
+++ b/indra/llui/llaccordionctrl.cpp
@@ -2,31 +2,25 @@
* @file llaccordionctrl.cpp
* @brief Accordion panel implementation
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&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://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * 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://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * 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.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
@@ -63,8 +57,16 @@ static LLDefaultChildRegistry::Register<LLAccordionCtrl> t2("accordion");
LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
, mFitParent(params.fit_parent)
+ , mAutoScrolling( false )
+ , mAutoScrollRate( 0.f )
+ , mSelectedTab( NULL )
+ , mTabComparator( NULL )
+ , mNoVisibleTabsHelpText(NULL)
+ , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString())
{
- mSingleExpansion = params.single_expansion;
+ initNoTabsWidget(params.no_matched_tabs_text);
+
+ mSingleExpansion = params.single_expansion;
if(mFitParent && !mSingleExpansion)
{
llinfos << "fit_parent works best when combined with single_expansion" << llendl;
@@ -72,7 +74,13 @@ LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
}
LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
+ , mAutoScrolling( false )
+ , mAutoScrollRate( 0.f )
+ , mSelectedTab( NULL )
+ , mNoVisibleTabsHelpText(NULL)
{
+ initNoTabsWidget(LLTextBox::Params());
+
mSingleExpansion = false;
mFitParent = false;
LLUICtrlFactory::getInstance()->buildPanel(this, "accordion_parent.xml");
@@ -81,6 +89,19 @@ LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
//---------------------------------------------------------------------------------
void LLAccordionCtrl::draw()
{
+ if (mAutoScrolling)
+ {
+ // add acceleration to autoscroll
+ mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
+ }
+ else
+ {
+ // reset to minimum for next time
+ mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
+ }
+ // clear this flag to be set on next call to autoScroll
+ mAutoScrolling = false;
+
LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
LLLocalClipRect clip(local_rect);
@@ -101,7 +122,6 @@ BOOL LLAccordionCtrl::postBuild()
scrollbar_size,
getRect().getHeight() - 1);
-
LLScrollbar::Params sbparams;
sbparams.name("scrollable vertical");
sbparams.rect(scroll_rect);
@@ -150,6 +170,8 @@ BOOL LLAccordionCtrl::postBuild()
}
}
+ updateNoTabsHelpTextVisibility();
+
return TRUE;
}
@@ -169,8 +191,15 @@ void LLAccordionCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
rcLocal.mRight = rcLocal.mLeft + width;
rcLocal.mTop = rcLocal.mBottom + height;
+ // get textbox a chance to reshape its content
+ mNoVisibleTabsHelpText->reshape(width, height, called_from_parent);
+
setRect(rcLocal);
+ // assume that help text is always fit accordion.
+ // necessary text paddings can be set via h_pad and v_pad
+ mNoVisibleTabsHelpText->setRect(getLocalRect());
+
arrange();
}
@@ -313,44 +342,136 @@ void LLAccordionCtrl::addCollapsibleCtrl(LLView* view)
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
if(!accordion_tab)
return;
- if(std::find(getChildList()->begin(),getChildList()->end(),accordion_tab) == getChildList()->end())
+ if(std::find(beginChild(), endChild(), accordion_tab) == endChild())
addChild(accordion_tab);
mAccordionTabs.push_back(accordion_tab);
-
+
accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, mAccordionTabs.size() - 1) );
+ arrange();
+}
+void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view)
+{
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
+ if(!accordion_tab)
+ return;
+
+ if(std::find(beginChild(), endChild(), accordion_tab) != endChild())
+ removeChild(accordion_tab);
+
+ for (std::vector<LLAccordionCtrlTab*>::iterator iter = mAccordionTabs.begin();
+ iter != mAccordionTabs.end(); ++iter)
+ {
+ if (accordion_tab == (*iter))
+ {
+ mAccordionTabs.erase(iter);
+ break;
+ }
+ }
+
+ // if removed is selected - reset selection
+ if (mSelectedTab == view)
+ {
+ mSelectedTab = NULL;
+ }
}
+void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params)
+{
+ LLTextBox::Params tp = tb_params;
+ tp.rect(getLocalRect());
+ mNoMatchedTabsOrigString = tp.initial_value().asString();
+ mNoVisibleTabsHelpText = LLUICtrlFactory::create<LLTextBox>(tp, this);
+}
-void LLAccordionCtrl::arrange()
+void LLAccordionCtrl::updateNoTabsHelpTextVisibility()
{
- if( mAccordionTabs.size() == 0)
+ bool visible_exists = false;
+ std::vector<LLAccordionCtrlTab*>::const_iterator it = mAccordionTabs.begin();
+ const std::vector<LLAccordionCtrlTab*>::const_iterator it_end = mAccordionTabs.end();
+ for (; it != it_end; ++it)
{
- //We do not arrange if we do not have what should be arranged
- return;
+ if ((*it)->getVisible())
+ {
+ visible_exists = true;
+ break;
+ }
}
- //Calculate params
+ mNoVisibleTabsHelpText->setVisible(!visible_exists);
+}
+
+void LLAccordionCtrl::arrangeSinge()
+{
S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+ S32 panel_height;
-
- if(mAccordionTabs.size() == 1)
+ S32 collapsed_height = 0;
+
+ for(size_t i=0;i<mAccordionTabs.size();++i)
{
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
-
- LLRect panel_rect = accordion_tab->getRect();
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN;
+ if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
+ continue;
+ if(!accordion_tab->isExpanded() )
+ {
+ collapsed_height+=mAccordionTabs[i]->getRect().getHeight();
+ }
+ }
- ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
+ S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height;
+
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
- return;
+ if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
+ continue;
+ if(!accordion_tab->isExpanded() )
+ {
+ panel_height = accordion_tab->getRect().getHeight();
+ }
+ else
+ {
+ if(mFitParent)
+ {
+ panel_height = expanded_height;
+ }
+ else
+ {
+ if(accordion_tab->getAccordionView())
+ {
+ panel_height = accordion_tab->getAccordionView()->getRect().getHeight() +
+ accordion_tab->getHeaderHeight() + 2*BORDER_MARGIN;
+ }
+ else
+ {
+ panel_height = accordion_tab->getRect().getHeight();
+ }
+ }
+ }
+
+ // make sure at least header is shown
+ panel_height = llmax(panel_height, accordion_tab->getHeaderHeight());
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
+ panel_top-=mAccordionTabs[i]->getRect().getHeight();
}
+ show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
+ updateLayout(getRect().getWidth(), getRect().getHeight());
+}
+
+void LLAccordionCtrl::arrangeMultiple()
+{
+ S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+
+ //Calculate params
for(size_t i = 0; i < mAccordionTabs.size(); i++ )
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
@@ -398,7 +519,44 @@ void LLAccordionCtrl::arrange()
show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
updateLayout(getRect().getWidth(),getRect().getHeight());
+}
+
+void LLAccordionCtrl::arrange()
+{
+ updateNoTabsHelpTextVisibility();
+
+ if( mAccordionTabs.size() == 0)
+ {
+ //We do not arrange if we do not have what should be arranged
+ return;
+ }
+
+
+ if(mAccordionTabs.size() == 1)
+ {
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel
+
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
+
+ LLRect panel_rect = accordion_tab->getRect();
+
+ S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN;
+
+ if (accordion_tab->getFitParent())
+ panel_height = accordion_tab->getRect().getHeight();
+ ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
+
+ show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
+ return;
+
+ }
+
+ if(mSingleExpansion)
+ arrangeSinge ();
+ else
+ arrangeMultiple ();
}
//---------------------------------------------------------------------------------
@@ -420,6 +578,64 @@ BOOL LLAccordionCtrl::handleKeyHere (KEY key, MASK mask)
return LLPanel::handleKeyHere(key,mask);
}
+BOOL LLAccordionCtrl::handleDragAndDrop (S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Scroll folder view if needed. Never accepts a drag or drop.
+ *accept = ACCEPT_NO;
+ BOOL handled = autoScroll(x, y);
+
+ if( !handled )
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+ return TRUE;
+}
+
+BOOL LLAccordionCtrl::autoScroll (S32 x, S32 y)
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ bool scrolling = false;
+ if( mScrollbar->getVisible() )
+ {
+ LLRect rect_local( 0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0 );
+ LLRect screen_local_extents;
+
+ // clip rect against root view
+ screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
+ rect_local.intersectWith(screen_local_extents);
+
+ // autoscroll region should take up no more than one third of visible scroller area
+ S32 auto_scroll_region_height = llmin(rect_local.getHeight() / 3, 10);
+ S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
+
+ LLRect bottom_scroll_rect = screen_local_extents;
+ bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height;
+ if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax()) )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + auto_scroll_speed );
+ mAutoScrolling = true;
+ scrolling = true;
+ }
+
+ LLRect top_scroll_rect = screen_local_extents;
+ top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height;
+ if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() > 0) )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - auto_scroll_speed );
+ mAutoScrolling = true;
+ scrolling = true;
+ }
+ }
+ return scrolling;
+}
+
void LLAccordionCtrl::updateLayout (S32 width, S32 height)
{
S32 panel_top = height - BORDER_MARGIN ;
@@ -500,19 +716,60 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info)
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
if(accordion_tab->hasFocus() && i>0)
{
+ bool prev_visible_tab_found = false;
while(i>0)
{
if(mAccordionTabs[--i]->getVisible())
+ {
+ prev_visible_tab_found = true;
break;
+ }
+ }
+
+ if (prev_visible_tab_found)
+ {
+ accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ accordion_tab->notify(LLSD().with("action","select_last"));
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+ }
+ else if(str_action == "select_current")
+ {
+ for(size_t i=0;i<mAccordionTabs.size();++i)
+ {
+ // Set selection to the currently focused tab.
+ if(mAccordionTabs[i]->hasFocus())
+ {
+ if (mAccordionTabs[i] != mSelectedTab)
+ {
+ if (mSelectedTab)
+ {
+ mSelectedTab->setSelected(false);
+ }
+ mSelectedTab = mAccordionTabs[i];
+ mSelectedTab->setSelected(true);
}
-
- accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- accordion_tab->notify(LLSD().with("action","select_last"));
+
return 1;
}
}
return 0;
}
+ else if(str_action == "deselect_current")
+ {
+ // Reset selection to the currently selected tab.
+ if (mSelectedTab)
+ {
+ mSelectedTab->setSelected(false);
+ mSelectedTab = NULL;
+ return 1;
+ }
+ return 0;
+ }
}
else if (info.has("scrollToShowRect"))
{
@@ -537,6 +794,20 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info)
}
return 1;
}
+ else if (info.has("child_visibility_change"))
+ {
+ BOOL new_visibility = info["child_visibility_change"];
+ if (new_visibility)
+ {
+ // there is at least one visible tab
+ mNoVisibleTabsHelpText->setVisible(FALSE);
+ }
+ else
+ {
+ // it could be the latest visible tab, check all of them
+ updateNoTabsHelpTextVisibility();
+ }
+ }
return LLPanel::notifyParent(info);
}
void LLAccordionCtrl::reset ()
@@ -545,6 +816,71 @@ void LLAccordionCtrl::reset ()
mScrollbar->setDocPos(0);
}
+void LLAccordionCtrl::expandDefaultTab()
+{
+ if (mAccordionTabs.size() > 0)
+ {
+ LLAccordionCtrlTab* tab = mAccordionTabs.front();
+
+ if (!tab->getDisplayChildren())
+ {
+ tab->setDisplayChildren(true);
+ }
+
+ for (size_t i = 1; i < mAccordionTabs.size(); ++i)
+ {
+ tab = mAccordionTabs[i];
+
+ if (tab->getDisplayChildren())
+ {
+ tab->setDisplayChildren(false);
+ }
+ }
+
+ arrange();
+ }
+}
+
+void LLAccordionCtrl::sort()
+{
+ if (!mTabComparator)
+ {
+ llwarns << "No comparator specified for sorting accordion tabs." << llendl;
+ return;
+ }
+
+ std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator));
+ arrange();
+}
+
+void LLAccordionCtrl::setFilterSubString(const std::string& filter_string)
+{
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
+ std::string text = filter_string.empty() ? mNoVisibleTabsOrigString : mNoMatchedTabsOrigString;
+ LLStringUtil::format(text, args);
+
+ mNoVisibleTabsHelpText->setValue(text);
+}
+
+const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const
+{
+ typedef std::vector<LLAccordionCtrlTab*>::const_iterator tabs_const_iterator;
+
+ const LLAccordionCtrlTab* result = 0;
+
+ for (tabs_const_iterator i = mAccordionTabs.begin(); i != mAccordionTabs.end(); ++i)
+ {
+ if ((*i)->isExpanded())
+ {
+ result = *i;
+ break;
+ }
+ }
+
+ return result;
+}
+
S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */)
{
if(tab_index < 0)