Skip to content
Snippets Groups Projects
Commit a4baeed1 authored by Lynx Linden's avatar Lynx Linden
Browse files

EXT-3929: Moved LLAccordionCtrl to llui.

Both LLAccordionCtrl and LLAccordionCtrlTab should live in llui, not
newview. I need to make this happen in order to fix EXT-3929. No code
change was necessary (other than to change #include for predefined
headers) as these classes did not rely on any newview code.
parent df08485c
No related branches found
No related tags found
No related merge requests found
...@@ -26,6 +26,8 @@ include_directories( ...@@ -26,6 +26,8 @@ include_directories(
) )
set(llui_SOURCE_FILES set(llui_SOURCE_FILES
llaccordionctrl.cpp
llaccordionctrltab.cpp
llbutton.cpp llbutton.cpp
llcheckboxctrl.cpp llcheckboxctrl.cpp
llclipboard.cpp llclipboard.cpp
...@@ -111,6 +113,8 @@ set(llui_SOURCE_FILES ...@@ -111,6 +113,8 @@ set(llui_SOURCE_FILES
set(llui_HEADER_FILES set(llui_HEADER_FILES
CMakeLists.txt CMakeLists.txt
llaccordionctrl.h
llaccordionctrltab.h
llbutton.h llbutton.h
llcallbackmap.h llcallbackmap.h
llcheckboxctrl.h llcheckboxctrl.h
......
/**
* @file llaccordionctrl.cpp
* @brief Accordion panel implementation
*
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
* Copyright (c) 2009, Linden Research, Inc.
*
* 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
*
* 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
*
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llaccordionctrl.h"
#include "llaccordionctrltab.h"
#include "lluictrlfactory.h" // builds floaters from XML
#include "llwindow.h"
#include "llfocusmgr.h"
#include "lllocalcliprect.h"
#include "boost/bind.hpp"
static const S32 DRAGGER_BAR_MARGIN = 4;
static const S32 DRAGGER_BAR_HEIGHT = 5;
static const S32 BORDER_MARGIN = 2;
static const S32 PARENT_BORDER_MARGIN = 5;
static const S32 panel_delta = DRAGGER_BAR_MARGIN; // Distanse between two panels
static const S32 HORIZONTAL_MULTIPLE = 8;
static const S32 VERTICAL_MULTIPLE = 16;
static const F32 MIN_AUTO_SCROLL_RATE = 120.f;
static const F32 MAX_AUTO_SCROLL_RATE = 500.f;
static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
// LLAccordionCtrl =================================================================|
static LLDefaultChildRegistry::Register<LLAccordionCtrl> t2("accordion");
LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
, mFitParent(params.fit_parent)
{
mSingleExpansion = params.single_expansion;
if(mFitParent && !mSingleExpansion)
{
llinfos << "fit_parent works best when combined with single_expansion" << llendl;
}
}
LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
{
mSingleExpansion = false;
mFitParent = false;
LLUICtrlFactory::getInstance()->buildPanel(this, "accordion_parent.xml");
}
//---------------------------------------------------------------------------------
void LLAccordionCtrl::draw()
{
LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
LLLocalClipRect clip(local_rect);
LLPanel::draw();
}
//---------------------------------------------------------------------------------
BOOL LLAccordionCtrl::postBuild()
{
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
LLRect scroll_rect;
scroll_rect.setOriginAndSize(
getRect().getWidth() - scrollbar_size,
1,
scrollbar_size,
getRect().getHeight() - 1);
LLScrollbar::Params sbparams;
sbparams.name("scrollable vertical");
sbparams.rect(scroll_rect);
sbparams.orientation(LLScrollbar::VERTICAL);
sbparams.doc_size(mInnerRect.getHeight());
sbparams.doc_pos(0);
sbparams.page_size(mInnerRect.getHeight());
sbparams.step_size(VERTICAL_MULTIPLE);
sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2));
mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
LLView::addChild( mScrollbar );
mScrollbar->setVisible( false );
mScrollbar->setFollowsRight();
mScrollbar->setFollowsTop();
mScrollbar->setFollowsBottom();
//if it was created from xml...
std::vector<LLUICtrl*> accordion_tabs;
for(child_list_const_iter_t it = getChildList()->begin();
getChildList()->end() != it; ++it)
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*it);
if(accordion_tab == NULL)
continue;
if(std::find(mAccordionTabs.begin(),mAccordionTabs.end(),accordion_tab) == mAccordionTabs.end())
{
accordion_tabs.push_back(accordion_tab);
}
}
for(std::vector<LLUICtrl*>::reverse_iterator it = accordion_tabs.rbegin();it!=accordion_tabs.rend();++it)
addCollapsibleCtrl(*it);
arrange ();
if(mSingleExpansion)
{
if(!mAccordionTabs[0]->getDisplayChildren())
mAccordionTabs[0]->setDisplayChildren(true);
for(size_t i=1;i<mAccordionTabs.size();++i)
{
if(mAccordionTabs[i]->getDisplayChildren())
mAccordionTabs[i]->setDisplayChildren(false);
}
}
return TRUE;
}
//---------------------------------------------------------------------------------
LLAccordionCtrl::~LLAccordionCtrl()
{
mAccordionTabs.clear();
}
//---------------------------------------------------------------------------------
void LLAccordionCtrl::reshape(S32 width, S32 height, BOOL called_from_parent)
{
// adjust our rectangle
LLRect rcLocal = getRect();
rcLocal.mRight = rcLocal.mLeft + width;
rcLocal.mTop = rcLocal.mBottom + height;
setRect(rcLocal);
arrange();
}
//---------------------------------------------------------------------------------
BOOL LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
return LLPanel::handleRightMouseDown(x, y, mask);
}
//---------------------------------------------------------------------------------
void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta)
{
for(size_t i = panel_num; i < mAccordionTabs.size(); i++ )
{
ctrlShiftVertical(mAccordionTabs[i],delta);
}
}
//---------------------------------------------------------------------------------
void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num)
{
if(mSingleExpansion)
{
for(size_t i=0;i<mAccordionTabs.size();++i)
{
if(i==panel_num)
continue;
if(mAccordionTabs[i]->getDisplayChildren())
mAccordionTabs[i]->setDisplayChildren(false);
}
}
arrange();
}
void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height)
{
calcRecuiredHeight();
if(getRecuiredHeight() > height )
showScrollbar(width,height);
else
hideScrollbar(width,height);
}
void LLAccordionCtrl::showScrollbar(S32 width, S32 height)
{
bool was_visible = mScrollbar->getVisible();
mScrollbar->setVisible(true);
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
ctrlSetLeftTopAndSize(mScrollbar
,width-scrollbar_size - PARENT_BORDER_MARGIN/2
,height-PARENT_BORDER_MARGIN
,scrollbar_size
,height-2*PARENT_BORDER_MARGIN);
mScrollbar->setPageSize(height);
mScrollbar->setDocParams(mInnerRect.getHeight(),mScrollbar->getDocPos());
if(was_visible)
{
S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1);
mScrollbar->setDocPos(scroll_pos);
}
}
void LLAccordionCtrl::hideScrollbar( S32 width, S32 height )
{
if(mScrollbar->getVisible() == false)
return;
mScrollbar->setVisible(false);
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
S32 panel_width = width - 2*BORDER_MARGIN;
//reshape all accordeons and shift all draggers
for(size_t i=0;i<mAccordionTabs.size();++i)
{
LLRect panel_rect = mAccordionTabs[i]->getRect();
ctrlSetLeftTopAndSize(mAccordionTabs[i],panel_rect.mLeft,panel_rect.mTop,panel_width,panel_rect.getHeight());
}
mScrollbar->setDocPos(0);
if(mAccordionTabs.size()>0)
{
S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel
S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop;
shiftAccordionTabs(0,diff);
}
}
//---------------------------------------------------------------------------------
S32 LLAccordionCtrl::calcRecuiredHeight()
{
S32 rec_height = 0;
std::vector<LLAccordionCtrlTab*>::iterator panel;
for(panel=mAccordionTabs.begin(); panel!=mAccordionTabs.end(); ++panel)
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*panel);
if(accordion_tab && accordion_tab->getVisible())
{
rec_height += accordion_tab->getRect().getHeight();
}
}
mInnerRect.setLeftTopAndSize(0,rec_height + BORDER_MARGIN*2,getRect().getWidth(),rec_height + BORDER_MARGIN);
return mInnerRect.getHeight();
}
//---------------------------------------------------------------------------------
void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
{
if(!panel)
return;
LLRect panel_rect = panel->getRect();
panel_rect.setLeftTopAndSize( left, top, width, height);
panel->reshape( width, height, 1);
panel->setRect(panel_rect);
}
void LLAccordionCtrl::ctrlShiftVertical(LLView* panel,S32 delta)
{
if(!panel)
return;
panel->translate(0,delta);
}
//---------------------------------------------------------------------------------
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())
addChild(accordion_tab);
mAccordionTabs.push_back(accordion_tab);
accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, mAccordionTabs.size() - 1) );
}
void LLAccordionCtrl::arrange()
{
if( mAccordionTabs.size() == 0)
{
//We do not arrange if we do not have what should be arranged
return;
}
//Calculate params
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
if(mAccordionTabs.size() == 1)
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
LLRect panel_rect = accordion_tab->getRect();
S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN;
ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height);
show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
return;
}
for(size_t i = 0; i < mAccordionTabs.size(); i++ )
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
if(accordion_tab->getVisible() == false) //skip hidden accordion tabs
continue;
if(!accordion_tab->isExpanded() )
{
ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight());
panel_top-=mAccordionTabs[i]->getRect().getHeight();
}
else
{
S32 panel_height = accordion_tab->getRect().getHeight();
if(mFitParent)
{
// all expanded tabs will have equal height
panel_height = calcExpandedTabHeight(i, panel_top);
ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height);
// try to make accordion tab fit accordion view height.
// Accordion View should implement getRequiredRect() and provide valid height
S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight();
optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN;
if(optimal_height < panel_height)
{
panel_height = optimal_height;
}
// minimum tab height is equal to header height
if(mAccordionTabs[i]->getHeaderHeight() > panel_height)
{
panel_height = mAccordionTabs[i]->getHeaderHeight();
}
}
ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
panel_top-=panel_height;
}
}
show_hide_scrollbar(getRect().getWidth(),getRect().getHeight());
updateLayout(getRect().getWidth(),getRect().getHeight());
}
//---------------------------------------------------------------------------------
BOOL LLAccordionCtrl::handleScrollWheel ( S32 x, S32 y, S32 clicks )
{
if(LLPanel::handleScrollWheel(x,y,clicks))
return TRUE;
if( mScrollbar->getVisible() && mScrollbar->handleScrollWheel( 0, 0, clicks ) )
return TRUE;
return false;
}
BOOL LLAccordionCtrl::handleKeyHere (KEY key, MASK mask)
{
if( mScrollbar->getVisible() && mScrollbar->handleKeyHere( key,mask ) )
return TRUE;
return LLPanel::handleKeyHere(key,mask);
}
void LLAccordionCtrl::updateLayout (S32 width, S32 height)
{
S32 panel_top = height - BORDER_MARGIN ;
if(mScrollbar->getVisible())
panel_top+=mScrollbar->getDocPos();
S32 panel_width = width - 2*BORDER_MARGIN;
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
if(mScrollbar->getVisible())
panel_width-=scrollbar_size;
//set sizes for first panels and dragbars
for(size_t i=0;i<mAccordionTabs.size();++i)
{
if(!mAccordionTabs[i]->getVisible())
continue;
LLRect panel_rect = mAccordionTabs[i]->getRect();
ctrlSetLeftTopAndSize(mAccordionTabs[i],panel_rect.mLeft,panel_top,panel_width,panel_rect.getHeight());
panel_top-=panel_rect.getHeight();
}
}
void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*)
{
updateLayout(getRect().getWidth(),getRect().getHeight());
}
void LLAccordionCtrl::onOpen (const LLSD& key)
{
for(size_t i=0;i<mAccordionTabs.size();++i)
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
LLPanel* panel = dynamic_cast<LLPanel*>(accordion_tab->getAccordionView());
if(panel!=NULL)
{
panel->onOpen(key);
}
}
}
S32 LLAccordionCtrl::notifyParent(const LLSD& info)
{
if(info.has("action"))
{
std::string str_action = info["action"];
if(str_action == "size_changes")
{
//
arrange();
return 1;
}
else if(str_action == "select_next")
{
for(size_t i=0;i<mAccordionTabs.size();++i)
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
if(accordion_tab->hasFocus())
{
while(++i<mAccordionTabs.size())
{
if(mAccordionTabs[i]->getVisible())
break;
}
if(i<mAccordionTabs.size())
{
accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
accordion_tab->notify(LLSD().with("action","select_first"));
return 1;
}
break;
}
}
return 0;
}
else if(str_action == "select_prev")
{
for(size_t i=0;i<mAccordionTabs.size();++i)
{
LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
if(accordion_tab->hasFocus() && i>0)
{
while(i>0)
{
if(mAccordionTabs[--i]->getVisible())
break;
}
accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
accordion_tab->notify(LLSD().with("action","select_last"));
return 1;
}
}
return 0;
}
}
else if (info.has("scrollToShowRect"))
{
LLRect screen_rc, local_rc;
screen_rc.setValue(info["scrollToShowRect"]);
screenRectToLocal(screen_rc, &local_rc);
// Translate to parent coordinatess to check if we are in visible rectangle
local_rc.translate( getRect().mLeft, getRect().mBottom );
if ( !getRect().contains (local_rc) )
{
// Back to local coords and calculate position for scroller
S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom;
S32 top = mScrollbar->getDocPos() - local_rc.mTop + getRect().mTop;
S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
bottom, // min vertical scroll
top); // max vertical scroll
mScrollbar->setDocPos( scroll_pos );
}
return 1;
}
return LLPanel::notifyParent(info);
}
void LLAccordionCtrl::reset ()
{
if(mScrollbar)
mScrollbar->setDocPos(0);
}
S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */)
{
if(tab_index < 0)
{
return available_height;
}
S32 collapsed_tabs_height = 0;
S32 num_expanded = 0;
for(size_t n = tab_index; n < mAccordionTabs.size(); ++n)
{
if(!mAccordionTabs[n]->isExpanded())
{
collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight();
}
else
{
++num_expanded;
}
}
if(0 == num_expanded)
{
return available_height;
}
S32 expanded_tab_height = available_height - collapsed_tabs_height - BORDER_MARGIN; // top BORDER_MARGIN is added in arrange(), here we add bottom BORDER_MARGIN
expanded_tab_height /= num_expanded;
return expanded_tab_height;
}
/**
* @file LLAccordionCtrl.h
* @brief Accordion Panel implementation
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* 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
*
* 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
*
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_ACCORDIONCTRL_H
#define LL_ACCORDIONCTRL_H
#include "llpanel.h"
#include "llscrollbar.h"
#include <vector>
#include <algorithm>
#include <string>
class LLAccordionCtrlTab;
class LLAccordionCtrl: public LLPanel
{
private:
std::vector<LLAccordionCtrlTab*> mAccordionTabs;
void ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height);
void ctrlShiftVertical(LLView* panel,S32 delta);
void onCollapseCtrlCloseOpen(S16 panel_num);
void shiftAccordionTabs(S16 panel_num, S32 delta);
public:
struct Params
: public LLInitParam::Block<Params, LLPanel::Params>
{
Optional<bool> single_expansion,
fit_parent; /* Accordion will fit its parent size, controls that are placed into
accordion tabs are responsible for scrolling their content.
*NOTE fit_parent works best when combined with single_expansion.
Accordion view should implement getRequiredRect() and provide valid height*/
Params()
: single_expansion("single_expansion",false)
, fit_parent("fit_parent", false)
{};
};
LLAccordionCtrl(const Params& params);
LLAccordionCtrl();
virtual ~LLAccordionCtrl();
virtual BOOL postBuild();
virtual BOOL handleRightMouseDown ( S32 x, S32 y, MASK mask);
virtual BOOL handleScrollWheel ( S32 x, S32 y, S32 clicks );
virtual BOOL handleKeyHere (KEY key, MASK mask);
//
// Call reshape after changing splitter's size
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
void addCollapsibleCtrl(LLView* view);
void arrange();
void draw();
void onScrollPosChangeCallback(S32, LLScrollbar*);
void onOpen (const LLSD& key);
S32 notifyParent(const LLSD& info);
void reset ();
private:
// Calc Splitter's height that is necessary to display all child content
S32 calcRecuiredHeight();
S32 getRecuiredHeight() const { return mInnerRect.getHeight(); }
S32 calcExpandedTabHeight(S32 tab_index = 0, S32 available_height = 0);
void updateLayout (S32 width, S32 height);
void show_hide_scrollbar (S32 width, S32 height);
void showScrollbar (S32 width, S32 height);
void hideScrollbar (S32 width, S32 height);
private:
LLRect mInnerRect;
LLScrollbar* mScrollbar;
bool mSingleExpansion;
bool mFitParent;
};
#endif // LL_LLSPLITTER_H
/**
* @file LLAccordionCtrlTab.cpp
* @brief Collapsible control implementation
*
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
* Copyright (c) 2009, Linden Research, Inc.
*
* 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
*
* 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
*
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "lluictrl.h"
#include "llaccordionctrltab.h"
#include "lltextbox.h"
static const std::string DD_BUTTON_NAME = "dd_button";
static const std::string DD_TEXTBOX_NAME = "dd_textbox";
static const std::string DD_HEADER_NAME = "dd_header";
static const S32 HEADER_HEIGHT = 20;
static const S32 HEADER_IMAGE_LEFT_OFFSET = 5;
static const S32 HEADER_TEXT_LEFT_OFFSET = 30;
static LLDefaultChildRegistry::Register<LLAccordionCtrlTab> t1("accordion_tab");
class LLAccordionCtrlTab::LLAccordionCtrlTabHeader : public LLUICtrl
{
public:
friend class LLUICtrlFactory;
struct Params : public LLInitParam::Block<Params, LLAccordionCtrlTab::Params>
{
Params();
};
LLAccordionCtrlTabHeader(const LLAccordionCtrlTabHeader::Params& p);
virtual ~LLAccordionCtrlTabHeader();
virtual void draw();
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
virtual BOOL postBuild();
void setTitle(const std::string& title);
virtual void onMouseEnter(S32 x, S32 y, MASK mask);
virtual void onMouseLeave(S32 x, S32 y, MASK mask);
virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
private:
LLTextBox* mHeaderTextbox;
// Overlay images (arrows)
LLPointer<LLUIImage> mImageCollapsed;
LLPointer<LLUIImage> mImageExpanded;
LLPointer<LLUIImage> mImageCollapsedPressed;
LLPointer<LLUIImage> mImageExpandedPressed;
// Background images
LLPointer<LLUIImage> mImageHeader;
LLPointer<LLUIImage> mImageHeaderOver;
LLPointer<LLUIImage> mImageHeaderPressed;
LLPointer<LLUIImage> mImageHeaderFocused;
LLUIColor mHeaderBGColor;
bool mNeedsHighlight;
};
LLAccordionCtrlTab::LLAccordionCtrlTabHeader::Params::Params()
{
}
LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader(
const LLAccordionCtrlTabHeader::Params& p)
: LLUICtrl(p)
, mHeaderBGColor(p.header_bg_color())
,mNeedsHighlight(false),
mImageCollapsed(p.header_collapse_img),
mImageCollapsedPressed(p.header_collapse_img_pressed),
mImageExpanded(p.header_expand_img),
mImageExpandedPressed(p.header_expand_img_pressed),
mImageHeader(p.header_image),
mImageHeaderOver(p.header_image_over),
mImageHeaderPressed(p.header_image_pressed),
mImageHeaderFocused(p.header_image_focused)
{
LLTextBox::Params textboxParams;
textboxParams.name(DD_TEXTBOX_NAME);
textboxParams.initial_value(p.title());
textboxParams.text_color(p.header_text_color());
textboxParams.follows.flags(FOLLOWS_NONE);
textboxParams.font( p.font() );
textboxParams.font_shadow(LLFontGL::NO_SHADOW);
textboxParams.use_ellipses = true;
textboxParams.bg_visible = false;
textboxParams.mouse_opaque = false;
mHeaderTextbox = LLUICtrlFactory::create<LLTextBox>(textboxParams);
addChild(mHeaderTextbox);
}
LLAccordionCtrlTab::LLAccordionCtrlTabHeader::~LLAccordionCtrlTabHeader()
{
}
BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild()
{
return TRUE;
}
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title)
{
if(mHeaderTextbox)
mHeaderTextbox->setText(title);
}
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
{
S32 width = getRect().getWidth();
S32 height = getRect().getHeight();
gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get(),true);
LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
bool collapsible = (parent && parent->getCollapsible());
bool expanded = (parent && parent->getDisplayChildren());
// Handle overlay images, if needed
// Only show green "focus" background image if the accordion is open,
// because the user's mental model of focus is that it goes away after
// the accordion is closed.
if (getParent()->hasFocus()
&& !(collapsible && !expanded))
{
mImageHeaderFocused->draw(0,0,width,height);
}
else
{
mImageHeader->draw(0,0,width,height);
}
if(mNeedsHighlight)
{
mImageHeaderOver->draw(0,0,width,height);
}
if(collapsible)
{
LLPointer<LLUIImage> overlay_image;
if(expanded)
{
overlay_image = mImageExpanded;
}
else
{
overlay_image = mImageCollapsed;
}
overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET,
(height - overlay_image->getHeight()) / 2);
}
LLUICtrl::draw();
}
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
{
S32 header_height = mHeaderTextbox->getTextPixelHeight();
LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET,(height+header_height)/2 ,width,(height-header_height)/2);
mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
mHeaderTextbox->setRect(textboxRect);
}
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseEnter(S32 x, S32 y, MASK mask)
{
LLUICtrl::onMouseEnter(x, y, mask);
mNeedsHighlight = true;
}
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MASK mask)
{
LLUICtrl::onMouseLeave(x, y, mask);
mNeedsHighlight = false;
}
BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, BOOL called_from_parent)
{
if ( ( key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE)
{
return getParent()->handleKey(key, mask, called_from_parent);
}
return LLUICtrl::handleKey(key, mask, called_from_parent);
}
LLAccordionCtrlTab::Params::Params()
: title("title")
,display_children("expanded", true)
,header_height("header_height", HEADER_HEIGHT),
min_width("min_width", 0),
min_height("min_height", 0)
,collapsible("collapsible", true)
,header_bg_color("header_bg_color")
,dropdown_bg_color("dropdown_bg_color")
,header_visible("header_visible",true)
,padding_left("padding_left",2)
,padding_right("padding_right",2)
,padding_top("padding_top",2)
,padding_bottom("padding_bottom",2)
,header_expand_img("header_expand_img")
,header_expand_img_pressed("header_expand_img_pressed")
,header_collapse_img("header_collapse_img")
,header_collapse_img_pressed("header_collapse_img_pressed")
,header_image("header_image")
,header_image_over("header_image_over")
,header_image_pressed("header_image_pressed")
,header_image_focused("header_image_focused")
,header_text_color("header_text_color")
{
mouse_opaque(false);
}
LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p)
: LLUICtrl(p)
,mDisplayChildren(p.display_children)
,mCollapsible(p.collapsible)
,mExpandedHeight(0)
,mDropdownBGColor(p.dropdown_bg_color())
,mHeaderVisible(p.header_visible)
,mPaddingLeft(p.padding_left)
,mPaddingRight(p.padding_right)
,mPaddingTop(p.padding_top)
,mPaddingBottom(p.padding_bottom)
,mCanOpenClose(true)
{
mStoredOpenCloseState = false;
mWasStateStored = false;
mDropdownBGColor = LLColor4::white;
LLAccordionCtrlTabHeader::Params headerParams;
headerParams.name(DD_HEADER_NAME);
headerParams.title(p.title);
mHeader = LLUICtrlFactory::create<LLAccordionCtrlTabHeader>(headerParams);
addChild(mHeader, 1);
reshape(100, 200,FALSE);
}
LLAccordionCtrlTab::~LLAccordionCtrlTab()
{
}
void LLAccordionCtrlTab::setDisplayChildren(bool display)
{
mDisplayChildren = display;
LLRect rect = getRect();
rect.mBottom = rect.mTop - (getDisplayChildren() ?
mExpandedHeight : HEADER_HEIGHT);
setRect(rect);
for(child_list_const_iter_t it = getChildList()->begin();
getChildList()->end() != it; ++it)
{
LLView* child = *it;
if(DD_HEADER_NAME == child->getName())
continue;
child->setVisible(getDisplayChildren());
}
}
void LLAccordionCtrlTab::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
{
LLRect headerRect;
LLUICtrl::reshape(width, height, TRUE);
headerRect.setLeftTopAndSize(
0,height,width,HEADER_HEIGHT);
mHeader->setRect(headerRect);
mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
for(child_list_const_iter_t it = getChildList()->begin();
getChildList()->end() != it; ++it)
{
LLView* child = *it;
if(DD_HEADER_NAME == child->getName())
continue;
if(!child->getVisible())
continue;
LLRect childRect = child->getRect();
S32 childWidth = width - getPaddingLeft() - getPaddingRight();
S32 childHeight = height - getHeaderHeight() - getPaddingTop() - getPaddingBottom();
child->reshape(childWidth,childHeight);
childRect.setLeftTopAndSize(
getPaddingLeft(),
childHeight + getPaddingBottom(),
childWidth,
childHeight);
child->setRect(childRect);
break;//suppose that there is only one panel
}
}
void LLAccordionCtrlTab::changeOpenClose(bool is_open)
{
if(is_open)
mExpandedHeight = getRect().getHeight();
setDisplayChildren(!is_open);
reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
if (mCommitSignal)
{
(*mCommitSignal)(this, getDisplayChildren());
}
}
BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask)
{
if(mCollapsible && mHeaderVisible && mCanOpenClose)
{
if(y >= (getRect().getHeight() - HEADER_HEIGHT) )
{
LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
header->setFocus(true);
changeOpenClose(getDisplayChildren());
//reset stored state
mWasStateStored = false;
return TRUE;
}
}
return LLUICtrl::handleMouseDown(x,y,mask);
}
BOOL LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask)
{
return LLUICtrl::handleMouseUp(x,y,mask);
}
boost::signals2::connection LLAccordionCtrlTab::setDropDownStateChangedCallback(commit_callback_t cb)
{
return setCommitCallback(cb);
}
bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group)
{
if(DD_HEADER_NAME != child->getName())
{
reshape(child->getRect().getWidth() , child->getRect().getHeight() + HEADER_HEIGHT );
mExpandedHeight = getRect().getHeight();
}
bool res = LLUICtrl::addChild(child, tab_group);
if(DD_HEADER_NAME != child->getName())
{
if(!mCollapsible)
setDisplayChildren(true);
else
setDisplayChildren(getDisplayChildren());
}
return res;
}
void LLAccordionCtrlTab::setAccordionView(LLView* panel)
{
addChild(panel,0);
}
LLView* LLAccordionCtrlTab::getAccordionView()
{
for(child_list_const_iter_t it = getChildList()->begin();
getChildList()->end() != it; ++it)
{
LLView* child = *it;
if(DD_HEADER_NAME == child->getName())
continue;
if(!child->getVisible())
continue;
return child;
}
return NULL;
}
S32 LLAccordionCtrlTab::getHeaderHeight()
{
return mHeaderVisible?HEADER_HEIGHT:0;
}
void LLAccordionCtrlTab::setHeaderVisible(bool value)
{
if(mHeaderVisible == value)
return;
mHeaderVisible = value;
if(mHeader)
mHeader->setVisible(value);
reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
};
//vurtual
BOOL LLAccordionCtrlTab::postBuild()
{
mHeader->setVisible(mHeaderVisible);
return LLUICtrl::postBuild();
}
bool LLAccordionCtrlTab::notifyChildren (const LLSD& info)
{
if(info.has("action"))
{
std::string str_action = info["action"];
if(str_action == "store_state")
{
storeOpenCloseState();
return true;
}
if(str_action == "restore_state")
{
restoreOpenCloseState();
return true;
}
}
return LLUICtrl::notifyChildren(info);
}
S32 LLAccordionCtrlTab::notifyParent(const LLSD& info)
{
if(info.has("action"))
{
std::string str_action = info["action"];
if(str_action == "size_changes")
{
//
S32 height = info["height"];
height = llmax(height,10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom();
mExpandedHeight = height;
if(isExpanded())
{
LLRect panel_rect = getRect();
panel_rect.setLeftTopAndSize( panel_rect.mLeft, panel_rect.mTop, panel_rect.getWidth(), height);
reshape(getRect().getWidth(),height);
setRect(panel_rect);
}
//LLAccordionCtrl should rearrange accodion tab if one of accordion change its size
getParent()->notifyParent(info);
return 1;
}
else if(str_action == "select_prev")
{
showAndFocusHeader();
return 1;
}
}
return LLUICtrl::notifyParent(info);
}
S32 LLAccordionCtrlTab::notify(const LLSD& info)
{
if(info.has("action"))
{
std::string str_action = info["action"];
if(str_action == "select_first")
{
showAndFocusHeader();
return 1;
}
else if( str_action == "select_last" )
{
if(getDisplayChildren() == false)
{
showAndFocusHeader();
}
else
{
LLView* view = getAccordionView();
if(view)
view->notify(LLSD().with("action","select_last"));
}
}
}
return 0;
}
BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent)
{
LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
if( !header->hasFocus() )
return LLUICtrl::handleKey(key, mask, called_from_parent);
if ( (key == KEY_ADD || key == KEY_RIGHT)&& mask == MASK_NONE)
{
if(getDisplayChildren() == false)
{
changeOpenClose(getDisplayChildren());
return TRUE;
}
}
if ( (key == KEY_SUBTRACT || key == KEY_LEFT)&& mask == MASK_NONE)
{
if(getDisplayChildren() == true)
{
changeOpenClose(getDisplayChildren());
return TRUE;
}
}
if ( key == KEY_DOWN && mask == MASK_NONE)
{
//if collapsed go to the next accordion
if(getDisplayChildren() == false)
//we processing notifyParent so let call parent directly
getParent()->notifyParent(LLSD().with("action","select_next"));
else
{
getAccordionView()->notify(LLSD().with("action","select_first"));
}
return TRUE;
}
if ( key == KEY_UP && mask == MASK_NONE)
{
//go to the previous accordion
//we processing notifyParent so let call parent directly
getParent()->notifyParent(LLSD().with("action","select_prev"));
return TRUE;
}
return LLUICtrl::handleKey(key, mask, called_from_parent);
}
void LLAccordionCtrlTab::showAndFocusHeader()
{
LLAccordionCtrlTabHeader* header = getChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
header->setFocus(true);
LLRect screen_rc;
LLRect selected_rc = header->getRect();
localRectToScreen(selected_rc, &screen_rc);
notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));
}
void LLAccordionCtrlTab::storeOpenCloseState()
{
if(mWasStateStored)
return;
mStoredOpenCloseState = getDisplayChildren();
mWasStateStored = true;
}
void LLAccordionCtrlTab::restoreOpenCloseState()
{
if(!mWasStateStored)
return;
if(getDisplayChildren() != mStoredOpenCloseState)
{
changeOpenClose(getDisplayChildren());
}
mWasStateStored = false;
}
/**
* @file LLAccordionCtrlTab.h
* @brief Collapsible box control implementation
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* 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
*
* 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
*
* 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.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_ACCORDIONCTRLTAB_H_
#define LL_ACCORDIONCTRLTAB_H_
#include <string>
#include "llrect.h"
class LLUICtrl;
class LLUICtrlFactory;
class LLUIImage;
class LLButton;
class LLTextBox;
// LLAccordionCtrlTab is a container for other controls.
// It has a Header, by clicking on which hosted controls are shown or hidden.
// When hosted controls are show - LLAccordionCtrlTab is expanded.
// When hosted controls are hidden - LLAccordionCtrlTab is collapsed.
class LLAccordionCtrlTab : public LLUICtrl
{
// Interface
public:
struct Params
: public LLInitParam::Block<Params, LLUICtrl::Params>
{
Optional<bool> display_children, //expanded or collapsed after initialization
collapsible;
Optional<std::string> title;
Optional<S32> header_height,
min_width,
min_height;
// Overlay images (arrows on the left)
Mandatory<LLUIImage*> header_expand_img,
header_expand_img_pressed,
header_collapse_img,
header_collapse_img_pressed;
// Background images for the accordion tabs
Mandatory<LLUIImage*> header_image,
header_image_over,
header_image_pressed,
header_image_focused;
Optional<LLUIColor> header_bg_color,
header_text_color,
dropdown_bg_color;
Optional<bool> header_visible;
Optional<S32> padding_left;
Optional<S32> padding_right;
Optional<S32> padding_top;
Optional<S32> padding_bottom;
Params();
};
typedef LLDefaultChildRegistry child_registry_t;
virtual ~LLAccordionCtrlTab();
// Registers callback for expand/collapse events.
boost::signals2::connection setDropDownStateChangedCallback(commit_callback_t cb);
// Changes expand/collapse state
virtual void setDisplayChildren(bool display);
// Returns expand/collapse state
virtual bool getDisplayChildren() const {return mDisplayChildren;};
//set LLAccordionCtrlTab panel
void setAccordionView(LLView* panel);
LLView* getAccordionView();
bool getCollapsible() {return mCollapsible;};
void setCollapsible(bool collapsible) {mCollapsible = collapsible;};
void changeOpenClose(bool is_open);
void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close;};
virtual BOOL postBuild();
S32 notifyParent(const LLSD& info);
S32 notify(const LLSD& info);
bool notifyChildren(const LLSD& info);
void storeOpenCloseState ();
void restoreOpenCloseState ();
protected:
LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&);
friend class LLUICtrlFactory;
// Overrides
public:
// Call reshape after changing size
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
// Changes expand/collapse state and triggers expand/collapse callbacks
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
virtual bool addChild(LLView* child, S32 tab_group);
bool isExpanded() { return mDisplayChildren; }
S32 getHeaderHeight();
// Min size functions
void setHeaderVisible(bool value);
bool getHeaderVisible() { return mHeaderVisible;}
S32 mExpandedHeight; // Height of expanded ctrl.
// Used to restore height after expand.
S32 getPaddingLeft() const { return mPaddingLeft;}
S32 getPaddingRight() const { return mPaddingRight;}
S32 getPaddingTop() const { return mPaddingTop;}
S32 getPaddingBottom() const { return mPaddingBottom;}
void showAndFocusHeader();
private:
class LLAccordionCtrlTabHeader;
LLAccordionCtrlTabHeader* mHeader; //Header
bool mDisplayChildren; //Expanded/collapsed
bool mCollapsible;
bool mHeaderVisible;
bool mCanOpenClose;
S32 mPaddingLeft;
S32 mPaddingRight;
S32 mPaddingTop;
S32 mPaddingBottom;
bool mStoredOpenCloseState;
bool mWasStateStored;
LLUIColor mDropdownBGColor;
};
#endif
...@@ -63,8 +63,6 @@ include_directories( ...@@ -63,8 +63,6 @@ include_directories(
) )
set(viewer_SOURCE_FILES set(viewer_SOURCE_FILES
llaccordionctrl.cpp
llaccordionctrltab.cpp
llagent.cpp llagent.cpp
llagentaccess.cpp llagentaccess.cpp
llagentdata.cpp llagentdata.cpp
...@@ -569,8 +567,6 @@ endif (LINUX) ...@@ -569,8 +567,6 @@ endif (LINUX)
set(viewer_HEADER_FILES set(viewer_HEADER_FILES
CMakeLists.txt CMakeLists.txt
ViewerInstall.cmake ViewerInstall.cmake
llaccordionctrl.h
llaccordionctrltab.h
llagent.h llagent.h
llagentaccess.h llagentaccess.h
llagentdata.h llagentdata.h
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment