[XP Tools] Initial merge Cherry Pick Also modernize llfloaterauction internally, but leave the ui the same for now. Breaks out script_question_mute() in llviewermessage.cpp to better sync with upstream Adds support for UnknownScriptQuestion notification (translators need to translate this one~) RLVa note: Rewrote RLVa permissions handling block just a bit. Added 13 new capabilities from the XP Tools, I doubt all of them really exist. Minor update to LLComboBox, decided against implementing LLIconsComboBox for now. Modified LLExperienceLog::notify to lookup names and display them along with the slurls since our editors don't do that automatically. Experience tweak: Changed a few notify's to notifytips so that we can click the links to experience profiles from chat instead of via hacked in buttons Migrated LLFloaterCompileQueue to a proper Instance Tracker so we can call getKey Modernized LLSD, gives us reverse iterators and the new debugging impl. We needed the reverse iterators. Experience tweak: Added virtual destructors to responders. Updated llhandle.h to allow calling getDerivedHandle in public. Updated LLScrollContainer and LLScrollBar to be more modern. Added LLFlatListView/flat_list_view from upstream - these don't seem work though? Added some newer login/logout strings to strings.xml Thanks for the default timeout policies, Aleric~ To avoid needing to scroll through tabs, about land tabs now are as big as they need to be to display their labels, same on groups Group Members and Roles has been renamed to just Members because this allows the new Experiences tab enough room to display. Thanks to Henri Beauchamp (Cool VL Viewer) for the setupList augmentation. (without it, I'd still be stuck) Thanks to Shyotl for the helpsies~ Added the LSL constants, events, and functions that LL neglected to put in. Added click callbacks and name lookups for profile linky texts~ Merge is up to 22b4cdc Old TODO: Get the uis looking nice (profiles? Experiences... floater) - done Old TODO: Make sure flatlistviews look okay... - Not using Old TODO: Fix LLFloaterExperiencePicker, right now the panel does not show. - unsure Old TODO: Remove the llfloaterabout.cpp change. - done Merges llexperiencecache with upstream and unstable Introduces LLCoroResponder, TODO: Make everything use this. Updates Reporter floater to the latest, supports the new cap thingy Also adds these commits/changes: [XPTools] Double clicking experiences in namelists should open the profile Add List.CopyNames support for Experiences [XP Tools] Some UI work, I'll do more later [XPTools] More UI Stuff, Later is now! Allow getSLURL for experiences WIP Experience list menu Also make EXPERIENCE > OBJECT, because mainline started OBJECT already [XPTools] Add Experience support to Name UI [XPTools] Fix experience profile UI 9c3067e843265587e91c659200a8d783acf2d9b2 [XPTools] Fix experience location showing "last" and getting set to "last" [XPTools] Move Experiences floater from view menu to world menu [XPTools] Fix up more UI [XPTools] Fix experiences panels [XPTools] Hide pieces of the Experiences menu when they're not usable [XPTools] More UI work, mostly to get the menus working [XPTools] The events list is for events, not experiences, remove menu # Conflicts: # indra/llcommon/llsd.cpp - merge with unstable branch # indra/llmessage/message_prehash.cpp # indra/llmessage/message_prehash.h # indra/llui/llscrollbar.cpp # indra/llui/llscrollcontainer.cpp # indra/llui/llurlentry.cpp # indra/llui/llurlregistry.cpp # indra/newview/app_settings/keywords.ini # indra/newview/app_settings/settings.xml # indra/newview/llappviewer.cpp # indra/newview/llappviewer.h # indra/newview/llassetuploadresponders.cpp # indra/newview/llcompilequeue.* - merge stable # indra/newview/llfloaterabout.cpp # indra/newview/llfloaterland.* - merge unstable # indra/newview/llfloaterproperties.cpp # indra/newview/llfloaterregioninfo.* - merge unstable # indra/newview/llmenucommands.cpp - merge unstable # indra/newview/llpreviewscript.cpp - merge unstable # indra/newview/llviewermessage.cpp - merge unstable # indra/newview/llviewerregion.cpp - merge unstable # indra/newview/skins/default/textures/textures.xml - merge unstable # indra/newview/skins/default/xui/en-us/strings.xml - merge unstable
1904 lines
51 KiB
C++
1904 lines
51 KiB
C++
/**
|
|
* @file lltabcontainer.cpp
|
|
* @brief LLTabContainer class
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2001-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 "lltabcontainer.h"
|
|
#include "llfocusmgr.h"
|
|
#include "llbutton.h"
|
|
#include "lllocalcliprect.h"
|
|
#include "llrect.h"
|
|
#include "llresmgr.h"
|
|
#include "llresizehandle.h"
|
|
#include "lltextbox.h"
|
|
#include "lltrans.h"
|
|
#include "llcriticaldamp.h"
|
|
#include "lluictrlfactory.h"
|
|
#include "llrender.h"
|
|
#include "llmultifloater.h"
|
|
|
|
const F32 SCROLL_STEP_TIME = 0.4f;
|
|
const F32 SCROLL_DELAY_TIME = 0.5f;
|
|
const S32 TAB_PADDING = 15;
|
|
const S32 TABCNTR_TAB_MIN_WIDTH = 60;
|
|
const S32 TABCNTR_VERT_TAB_MIN_WIDTH = 100;
|
|
const S32 TABCNTR_TAB_MAX_WIDTH = 150;
|
|
const S32 TABCNTR_TAB_PARTIAL_WIDTH = 12; // When tabs are parially obscured, how much can you still see.
|
|
const S32 TABCNTR_TAB_HEIGHT = 16;
|
|
const S32 TABCNTR_ARROW_BTN_SIZE = 16;
|
|
const S32 TABCNTR_BUTTON_PANEL_OVERLAP = 1; // how many pixels the tab buttons and tab panels overlap.
|
|
const S32 TABCNTR_TAB_H_PAD = 4;
|
|
|
|
const S32 TABCNTR_CLOSE_BTN_SIZE = 16;
|
|
const S32 TABCNTR_HEADER_HEIGHT = LLPANEL_BORDER_WIDTH + TABCNTR_CLOSE_BTN_SIZE;
|
|
|
|
const S32 TABCNTRV_CLOSE_BTN_SIZE = 16;
|
|
const S32 TABCNTRV_HEADER_HEIGHT = LLPANEL_BORDER_WIDTH + TABCNTRV_CLOSE_BTN_SIZE;
|
|
//const S32 TABCNTRV_TAB_WIDTH = 100;
|
|
const S32 TABCNTRV_ARROW_BTN_SIZE = 16;
|
|
const S32 TABCNTRV_PAD = 0;
|
|
|
|
static LLRegisterWidget<LLTabContainer> r("tab_container");
|
|
|
|
// Structure used to map tab buttons to and from tab panels
|
|
class LLTabTuple
|
|
{
|
|
public:
|
|
LLTabTuple( LLTabContainer* c, LLPanel* p, LLButton* b, LLTextBox* placeholder = NULL)
|
|
:
|
|
mTabContainer(c),
|
|
mTabPanel(p),
|
|
mButton(b),
|
|
mOldState(FALSE),
|
|
mPlaceholderText(placeholder),
|
|
mPadding(0)
|
|
{}
|
|
|
|
LLTabContainer* mTabContainer;
|
|
LLPanel* mTabPanel;
|
|
LLButton* mButton;
|
|
BOOL mOldState;
|
|
LLTextBox* mPlaceholderText;
|
|
S32 mPadding;
|
|
};
|
|
|
|
LLTabContainer::LLTabContainer(const std::string& name, const LLRect& rect, TabPosition pos,
|
|
BOOL bordered, BOOL is_vertical )
|
|
:
|
|
LLPanel(name, rect, bordered),
|
|
mCurrentTabIdx(-1),
|
|
mTabsHidden(FALSE),
|
|
mScrolled(FALSE),
|
|
mScrollPos(0),
|
|
mScrollPosPixels(0),
|
|
mMaxScrollPos(0),
|
|
mTitleBox(NULL),
|
|
mTopBorderHeight(LLPANEL_BORDER_WIDTH),
|
|
mTabPosition(pos),
|
|
mLockedTabCount(0),
|
|
mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
|
|
mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
|
|
mPrevArrowBtn(NULL),
|
|
mNextArrowBtn(NULL),
|
|
mIsVertical(is_vertical),
|
|
// Horizontal Specific
|
|
mJumpPrevArrowBtn(NULL),
|
|
mJumpNextArrowBtn(NULL),
|
|
mRightTabBtnOffset(0),
|
|
mTotalTabWidth(0)
|
|
{
|
|
//RN: HACK to support default min width for legacy vertical tab containers
|
|
if (mIsVertical)
|
|
{
|
|
mMinTabWidth = TABCNTR_VERT_TAB_MIN_WIDTH;
|
|
}
|
|
setMouseOpaque(FALSE);
|
|
initButtons( );
|
|
mDragAndDropDelayTimer.stop();
|
|
}
|
|
|
|
LLTabContainer::~LLTabContainer()
|
|
{
|
|
std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
|
|
}
|
|
|
|
//virtual
|
|
void LLTabContainer::setValue(const LLSD& value)
|
|
{
|
|
selectTab((S32) value.asInteger());
|
|
}
|
|
|
|
//virtual
|
|
void LLTabContainer::reshape(S32 width, S32 height, BOOL called_from_parent)
|
|
{
|
|
LLPanel::reshape( width, height, called_from_parent );
|
|
updateMaxScrollPos();
|
|
}
|
|
|
|
//virtual
|
|
LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
|
|
{
|
|
tuple_list_t::const_iterator itor;
|
|
for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
|
|
{
|
|
LLPanel *panel = (*itor)->mTabPanel;
|
|
if (panel->getName() == name)
|
|
{
|
|
return panel;
|
|
}
|
|
}
|
|
|
|
if (recurse)
|
|
{
|
|
for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
|
|
{
|
|
LLPanel *panel = (*itor)->mTabPanel;
|
|
LLView *child = panel->getChildView(name, recurse, FALSE);
|
|
if (child)
|
|
{
|
|
return child;
|
|
}
|
|
}
|
|
}
|
|
return LLView::getChildView(name, recurse, create_if_missing);
|
|
}
|
|
|
|
// virtual
|
|
void LLTabContainer::draw()
|
|
{
|
|
S32 target_pixel_scroll = 0;
|
|
S32 cur_scroll_pos = getScrollPos();
|
|
if (cur_scroll_pos > 0)
|
|
{
|
|
if (!mIsVertical)
|
|
{
|
|
S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
if (cur_scroll_pos == 0)
|
|
{
|
|
break;
|
|
}
|
|
target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
|
|
cur_scroll_pos--;
|
|
}
|
|
|
|
// Show part of the tab to the left of what is fully visible
|
|
target_pixel_scroll -= TABCNTR_TAB_PARTIAL_WIDTH;
|
|
// clamp so that rightmost tab never leaves right side of screen
|
|
target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
|
|
}
|
|
else
|
|
{
|
|
S32 available_height_with_arrows = getRect().getHeight() - getTopBorderHeight() - (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
if (cur_scroll_pos==0)
|
|
{
|
|
break;
|
|
}
|
|
target_pixel_scroll += (*iter)->mButton->getRect().getHeight();
|
|
cur_scroll_pos--;
|
|
}
|
|
S32 total_tab_height = (BTN_HEIGHT + TABCNTRV_PAD) * getTabCount() + TABCNTRV_PAD;
|
|
// clamp so that the bottom tab never leaves bottom of panel
|
|
target_pixel_scroll = llmin(total_tab_height - available_height_with_arrows, target_pixel_scroll);
|
|
}
|
|
}
|
|
|
|
setScrollPosPixels(mIsVertical ? target_pixel_scroll : lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLSmoothInterpolation::getInterpolant(0.08f)));
|
|
|
|
BOOL has_scroll_arrows = !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0));
|
|
if (!mIsVertical)
|
|
{
|
|
mJumpPrevArrowBtn->setVisible( has_scroll_arrows );
|
|
mJumpNextArrowBtn->setVisible( has_scroll_arrows );
|
|
}
|
|
mPrevArrowBtn->setVisible( has_scroll_arrows );
|
|
mNextArrowBtn->setVisible( has_scroll_arrows );
|
|
|
|
S32 left = 0, top = 0;
|
|
if (mIsVertical)
|
|
{
|
|
top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? TABCNTRV_ARROW_BTN_SIZE : 0);
|
|
top += getScrollPosPixels();
|
|
}
|
|
else
|
|
{
|
|
// Set the leftmost position of the tab buttons.
|
|
left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (TABCNTR_ARROW_BTN_SIZE * 2) : TABCNTR_TAB_H_PAD);
|
|
left -= getScrollPosPixels();
|
|
}
|
|
|
|
// Hide all the buttons
|
|
if (getTabsHidden() || mIsVertical)
|
|
{
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
tuple->mButton->setVisible( FALSE );
|
|
}
|
|
}
|
|
|
|
{
|
|
LLRect clip_rect = getLocalRect();
|
|
clip_rect.mLeft+=(LLPANEL_BORDER_WIDTH + 2);
|
|
clip_rect.mRight-=(LLPANEL_BORDER_WIDTH + 2);
|
|
LLLocalClipRect clip(clip_rect);
|
|
LLPanel::draw();
|
|
}
|
|
|
|
// if tabs are hidden, don't draw them and leave them in the invisible state
|
|
if (!getTabsHidden())
|
|
{
|
|
// Show all the buttons
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
tuple->mButton->setVisible( TRUE );
|
|
}
|
|
|
|
// Draw some of the buttons...
|
|
LLRect clip_rect = getLocalRect();
|
|
if (has_scroll_arrows && mIsVertical)
|
|
{
|
|
clip_rect.mTop = top - getScrollPosPixels();
|
|
clip_rect.mBottom = TABCNTR_ARROW_BTN_SIZE+1;
|
|
}
|
|
LLLocalClipRect clip(clip_rect);
|
|
|
|
S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos();
|
|
S32 idx = 0;
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
|
|
tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0,
|
|
top ? top - tuple->mButton->getRect().mTop : 0 );
|
|
if (top) top -= BTN_HEIGHT + TABCNTRV_PAD;
|
|
if (left) left += tuple->mButton->getRect().getWidth();
|
|
|
|
if (!mIsVertical)
|
|
{
|
|
if( idx < getScrollPos() )
|
|
{
|
|
if( tuple->mButton->getFlashing() )
|
|
{
|
|
mPrevArrowBtn->setFlashing( TRUE );
|
|
}
|
|
}
|
|
else if( max_scroll_visible < idx )
|
|
{
|
|
if( tuple->mButton->getFlashing() )
|
|
{
|
|
mNextArrowBtn->setFlashing( TRUE );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLUI::pushMatrix();
|
|
LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
|
|
tuple->mButton->draw();
|
|
LLUI::popMatrix();
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
|
|
|
|
if( mIsVertical && has_scroll_arrows )
|
|
{
|
|
// Redraw the arrows so that they appears on top.
|
|
gGL.pushUIMatrix();
|
|
gGL.translateUI((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f);
|
|
mPrevArrowBtn->draw();
|
|
gGL.popUIMatrix();
|
|
|
|
gGL.pushUIMatrix();
|
|
gGL.translateUI((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f);
|
|
mNextArrowBtn->draw();
|
|
gGL.popUIMatrix();
|
|
}
|
|
}
|
|
|
|
mPrevArrowBtn->setFlashing(FALSE);
|
|
mNextArrowBtn->setFlashing(FALSE);
|
|
}
|
|
|
|
|
|
// virtual
|
|
BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
|
|
{
|
|
BOOL handled = FALSE;
|
|
BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
|
|
|
|
if (has_scroll_arrows)
|
|
{
|
|
if (mJumpPrevArrowBtn&& mJumpPrevArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
|
|
handled = mJumpPrevArrowBtn->handleMouseDown(local_x, local_y, mask);
|
|
}
|
|
else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
|
|
handled = mJumpNextArrowBtn->handleMouseDown(local_x, local_y, mask);
|
|
}
|
|
else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
|
|
handled = mPrevArrowBtn->handleMouseDown(local_x, local_y, mask);
|
|
}
|
|
else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mNextArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mNextArrowBtn->getRect().mBottom;
|
|
handled = mNextArrowBtn->handleMouseDown(local_x, local_y, mask);
|
|
}
|
|
}
|
|
if (!handled)
|
|
{
|
|
handled = LLPanel::handleMouseDown( x, y, mask );
|
|
}
|
|
|
|
S32 tab_count = getTabCount();
|
|
if (tab_count > 0 && !getTabsHidden())
|
|
{
|
|
LLTabTuple* firsttuple = getTab(0);
|
|
LLRect tab_rect;
|
|
if (mIsVertical)
|
|
{
|
|
tab_rect = LLRect(firsttuple->mButton->getRect().mLeft,
|
|
has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - TABCNTRV_PAD : mPrevArrowBtn->getRect().mTop,
|
|
firsttuple->mButton->getRect().mRight,
|
|
has_scroll_arrows ? mNextArrowBtn->getRect().mTop + TABCNTRV_PAD : mNextArrowBtn->getRect().mBottom );
|
|
}
|
|
else
|
|
{
|
|
tab_rect = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft,
|
|
firsttuple->mButton->getRect().mTop,
|
|
has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight,
|
|
firsttuple->mButton->getRect().mBottom );
|
|
}
|
|
if( tab_rect.pointInRect( x, y ) )
|
|
{
|
|
S32 index = getCurrentPanelIndex();
|
|
index = llclamp(index, 0, tab_count-1);
|
|
LLButton* tab_button = getTab(index)->mButton;
|
|
gFocusMgr.setMouseCapture(this);
|
|
tab_button->setFocus(TRUE);
|
|
gFocusMgr.setKeyboardFocus(tab_button);
|
|
}
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
|
|
{
|
|
BOOL handled = FALSE;
|
|
BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
|
|
|
|
if (has_scroll_arrows)
|
|
{
|
|
if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
|
|
handled = mJumpPrevArrowBtn->handleHover(local_x, local_y, mask);
|
|
}
|
|
else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
|
|
handled = mJumpNextArrowBtn->handleHover(local_x, local_y, mask);
|
|
}
|
|
else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
|
|
handled = mPrevArrowBtn->handleHover(local_x, local_y, mask);
|
|
}
|
|
else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mNextArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mNextArrowBtn->getRect().mBottom;
|
|
handled = mNextArrowBtn->handleHover(local_x, local_y, mask);
|
|
}
|
|
}
|
|
if (!handled)
|
|
{
|
|
handled = LLPanel::handleHover(x, y, mask);
|
|
}
|
|
|
|
commitHoveredButton(x, y);
|
|
return handled;
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
|
|
{
|
|
BOOL handled = FALSE;
|
|
BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
|
|
|
|
if (has_scroll_arrows)
|
|
{
|
|
if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
|
|
handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
|
|
}
|
|
else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
|
|
handled = mJumpNextArrowBtn->handleMouseUp(local_x, local_y, mask);
|
|
}
|
|
else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
|
|
handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
|
|
}
|
|
else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mNextArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mNextArrowBtn->getRect().mBottom;
|
|
handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
|
|
}
|
|
}
|
|
if (!handled)
|
|
{
|
|
handled = LLPanel::handleMouseUp( x, y, mask );
|
|
}
|
|
|
|
commitHoveredButton(x, y);
|
|
LLPanel* cur_panel = getCurrentPanel();
|
|
if (hasMouseCapture())
|
|
{
|
|
if (cur_panel)
|
|
{
|
|
if (!cur_panel->focusFirstItem(FALSE))
|
|
{
|
|
// if nothing in the panel gets focus, make sure the new tab does
|
|
// otherwise the last tab might keep focus
|
|
getTab(getCurrentPanelIndex())->mButton->setFocus(TRUE);
|
|
}
|
|
}
|
|
gFocusMgr.setMouseCapture(NULL);
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLTabContainer::handleToolTip( S32 x, S32 y, std::string& msg, LLRect* sticky_rect )
|
|
{
|
|
BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect );
|
|
if (!handled && getTabCount() > 0 && !getTabsHidden())
|
|
{
|
|
LLTabTuple* firsttuple = getTab(0);
|
|
|
|
BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
|
|
LLRect clip;
|
|
if (mIsVertical)
|
|
{
|
|
clip = LLRect(firsttuple->mButton->getRect().mLeft,
|
|
has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - TABCNTRV_PAD : mPrevArrowBtn->getRect().mTop,
|
|
firsttuple->mButton->getRect().mRight,
|
|
has_scroll_arrows ? mNextArrowBtn->getRect().mTop + TABCNTRV_PAD : mNextArrowBtn->getRect().mBottom );
|
|
}
|
|
else
|
|
{
|
|
clip = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft,
|
|
firsttuple->mButton->getRect().mTop,
|
|
has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight,
|
|
firsttuple->mButton->getRect().mBottom );
|
|
}
|
|
|
|
if( clip.pointInRect( x, y ) )
|
|
{
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
tuple->mButton->setVisible( TRUE );
|
|
S32 local_x = x - tuple->mButton->getRect().mLeft;
|
|
S32 local_y = y - tuple->mButton->getRect().mBottom;
|
|
handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect );
|
|
if( handled )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask)
|
|
{
|
|
BOOL handled = FALSE;
|
|
if (key == KEY_LEFT && mask == MASK_ALT)
|
|
{
|
|
selectPrevTab();
|
|
handled = TRUE;
|
|
}
|
|
else if (key == KEY_RIGHT && mask == MASK_ALT)
|
|
{
|
|
selectNextTab();
|
|
handled = TRUE;
|
|
}
|
|
|
|
if (handled)
|
|
{
|
|
if (getCurrentPanel())
|
|
{
|
|
getCurrentPanel()->setFocus(TRUE);
|
|
}
|
|
}
|
|
|
|
if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
|
|
{
|
|
// if child has focus, but not the current panel, focus is on a button
|
|
if (mIsVertical)
|
|
{
|
|
switch(key)
|
|
{
|
|
case KEY_UP:
|
|
selectPrevTab();
|
|
handled = TRUE;
|
|
break;
|
|
case KEY_DOWN:
|
|
selectNextTab();
|
|
handled = TRUE;
|
|
break;
|
|
case KEY_LEFT:
|
|
handled = TRUE;
|
|
break;
|
|
case KEY_RIGHT:
|
|
if (getTabPosition() == LEFT && getCurrentPanel())
|
|
{
|
|
getCurrentPanel()->setFocus(TRUE);
|
|
}
|
|
handled = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(key)
|
|
{
|
|
case KEY_UP:
|
|
if (getTabPosition() == BOTTOM && getCurrentPanel())
|
|
{
|
|
getCurrentPanel()->setFocus(TRUE);
|
|
}
|
|
handled = TRUE;
|
|
break;
|
|
case KEY_DOWN:
|
|
if (getTabPosition() == TOP && getCurrentPanel())
|
|
{
|
|
getCurrentPanel()->setFocus(TRUE);
|
|
}
|
|
handled = TRUE;
|
|
break;
|
|
case KEY_LEFT:
|
|
selectPrevTab();
|
|
handled = TRUE;
|
|
break;
|
|
case KEY_RIGHT:
|
|
selectNextTab();
|
|
handled = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
// virtual
|
|
LLXMLNodePtr LLTabContainer::getXML(bool save_children) const
|
|
{
|
|
LLXMLNodePtr node = LLPanel::getXML();
|
|
|
|
node->setName(LL_TAB_CONTAINER_COMMON_TAG);
|
|
|
|
node->createChild("tab_position", TRUE)->setStringValue((getTabPosition() == TOP ? "top" : "bottom"));
|
|
return node;
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, std::string &tooltip)
|
|
{
|
|
BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
|
|
|
|
if(!getTabsHidden())
|
|
{
|
|
// In that case, we'll open the hovered tab while dragging and dropping items.
|
|
// This allows for drilling through tabs.
|
|
if (mDragAndDropDelayTimer.getStarted())
|
|
{
|
|
if (mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME)
|
|
{
|
|
if (has_scroll_arrows)
|
|
{
|
|
if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
|
|
mJumpPrevArrowBtn->handleHover(local_x, local_y, mask);
|
|
}
|
|
if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
|
|
mJumpNextArrowBtn->handleHover(local_x, local_y, mask);
|
|
}
|
|
if (mPrevArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
|
|
mPrevArrowBtn->handleHover(local_x, local_y, mask);
|
|
}
|
|
else if (mNextArrowBtn->getRect().pointInRect(x, y))
|
|
{
|
|
S32 local_x = x - mNextArrowBtn->getRect().mLeft;
|
|
S32 local_y = y - mNextArrowBtn->getRect().mBottom;
|
|
mNextArrowBtn->handleHover(local_x, local_y, mask);
|
|
}
|
|
}
|
|
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
tuple->mButton->setVisible( TRUE );
|
|
S32 local_x = x - tuple->mButton->getRect().mLeft;
|
|
S32 local_y = y - tuple->mButton->getRect().mBottom;
|
|
if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
|
|
{
|
|
tuple->mButton->onCommit();
|
|
}
|
|
}
|
|
// Stop the timer whether successful or not. Don't let it run forever.
|
|
mDragAndDropDelayTimer.stop();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Start a timer so we don't open tabs as soon as we hover on them
|
|
mDragAndDropDelayTimer.start();
|
|
}
|
|
}
|
|
|
|
return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip);
|
|
}
|
|
|
|
void LLTabContainer::addTabPanel(LLPanel* child,
|
|
const std::string& label,
|
|
BOOL select,
|
|
S32 indent,
|
|
BOOL placeholder,
|
|
eInsertionPoint insertion_point)
|
|
{
|
|
|
|
if (child->getParent() == this)
|
|
{
|
|
// already a child of mine
|
|
return;
|
|
}
|
|
const LLFontGL* font = LLResMgr::getInstance()->getRes( mIsVertical ? LLFONT_SANSSERIF : LLFONT_SANSSERIF_SMALL );
|
|
|
|
// Store the original label for possible xml export.
|
|
child->setLabel(label);
|
|
std::string trimmed_label = label;
|
|
LLStringUtil::trim(trimmed_label);
|
|
|
|
S32 button_width = mMinTabWidth;
|
|
if (!mIsVertical)
|
|
{
|
|
button_width = llclamp(font->getWidth(trimmed_label) + TAB_PADDING, mMinTabWidth, mMaxTabWidth);
|
|
}
|
|
|
|
// Tab panel
|
|
S32 tab_panel_top;
|
|
S32 tab_panel_bottom;
|
|
if (!getTabsHidden())
|
|
{
|
|
if( getTabPosition() == LLTabContainer::TOP )
|
|
{
|
|
S32 tab_height = mIsVertical ? BTN_HEIGHT : TABCNTR_TAB_HEIGHT;
|
|
tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - TABCNTR_BUTTON_PANEL_OVERLAP);
|
|
tab_panel_bottom = LLPANEL_BORDER_WIDTH;
|
|
}
|
|
else
|
|
{
|
|
tab_panel_top = getRect().getHeight() - getTopBorderHeight();
|
|
tab_panel_bottom = (TABCNTR_TAB_HEIGHT + LLPANEL_BORDER_WIDTH*2 - TABCNTR_BUTTON_PANEL_OVERLAP); // Run to the edge, covering up the border
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Scip tab button space if they are invisible(EXT - 576)
|
|
tab_panel_top = getRect().getHeight();
|
|
tab_panel_bottom = LLPANEL_BORDER_WIDTH;
|
|
}
|
|
LLRect tab_panel_rect;
|
|
if (!getTabsHidden() && mIsVertical)
|
|
{
|
|
tab_panel_rect = LLRect(mMinTabWidth + mRightTabBtnOffset + (LLPANEL_BORDER_WIDTH * 2) + TABCNTRV_PAD,
|
|
getRect().getHeight() - LLPANEL_BORDER_WIDTH,
|
|
getRect().getWidth() - LLPANEL_BORDER_WIDTH,
|
|
LLPANEL_BORDER_WIDTH);
|
|
}
|
|
else
|
|
{
|
|
tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH,
|
|
tab_panel_top,
|
|
getRect().getWidth()-LLPANEL_BORDER_WIDTH,
|
|
tab_panel_bottom );
|
|
}
|
|
child->setFollowsAll();
|
|
child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
|
|
child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
|
|
// add this child later
|
|
|
|
child->setVisible( FALSE ); // Will be made visible when selected
|
|
|
|
mTotalTabWidth += button_width;
|
|
|
|
// Tab button
|
|
LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw().
|
|
std::string tab_img;
|
|
std::string tab_selected_img;
|
|
S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel
|
|
|
|
if (mIsVertical)
|
|
{
|
|
btn_rect.setLeftTopAndSize(TABCNTRV_PAD + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
|
|
(getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + TABCNTRV_PAD) * getTabCount()),
|
|
mMinTabWidth,
|
|
BTN_HEIGHT);
|
|
}
|
|
else if( getTabPosition() == LLTabContainer::TOP )
|
|
{
|
|
btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
|
|
tab_img = "tab_top_blue.tga";
|
|
tab_selected_img = "tab_top_selected_blue.tga";
|
|
}
|
|
else
|
|
{
|
|
btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
|
|
tab_img = "tab_bottom_blue.tga";
|
|
tab_selected_img = "tab_bottom_selected_blue.tga";
|
|
}
|
|
|
|
LLTextBox* textbox = NULL;
|
|
LLButton* btn = NULL;
|
|
|
|
if (placeholder)
|
|
{
|
|
btn_rect.translate(0, -LLBUTTON_V_PAD-2);
|
|
textbox = new LLTextBox(trimmed_label, btn_rect, trimmed_label, font);
|
|
|
|
btn = new LLButton(LLStringUtil::null);
|
|
}
|
|
else
|
|
{
|
|
if (mIsVertical)
|
|
{
|
|
btn = new LLButton(std::string("vert tab button"),
|
|
btn_rect,
|
|
LLStringUtil::null,
|
|
LLStringUtil::null,
|
|
LLStringUtil::null,
|
|
NULL,
|
|
font,
|
|
trimmed_label, trimmed_label);
|
|
btn->setImages(std::string("tab_left.tga"), std::string("tab_left_selected.tga"));
|
|
btn->setScaleImage(TRUE);
|
|
btn->setHAlign(LLFontGL::LEFT);
|
|
btn->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
|
|
btn->setTabStop(FALSE);
|
|
if (indent)
|
|
{
|
|
btn->setLeftHPad(indent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
btn = new LLButton(std::string(child->getName()) + " tab",
|
|
btn_rect,
|
|
LLStringUtil::null, LLStringUtil::null, LLStringUtil::null,
|
|
NULL, // set userdata below
|
|
font,
|
|
trimmed_label, trimmed_label );
|
|
btn->setVisible( FALSE );
|
|
btn->setScaleImage(TRUE);
|
|
btn->setImages(tab_img, tab_selected_img);
|
|
|
|
// Try to squeeze in a bit more text
|
|
btn->setLeftHPad( 4 );
|
|
btn->setRightHPad( 2 );
|
|
btn->setHAlign(LLFontGL::LEFT);
|
|
btn->setTabStop(FALSE);
|
|
if (indent)
|
|
{
|
|
btn->setLeftHPad(indent);
|
|
}
|
|
|
|
if( getTabPosition() == TOP )
|
|
{
|
|
btn->setFollowsTop();
|
|
}
|
|
else
|
|
{
|
|
btn->setFollowsBottom();
|
|
}
|
|
}
|
|
std::string tooltip = trimmed_label;
|
|
LLStringUtil::format_map_t args;
|
|
args["[ALT]"] = LLTrans::getString(
|
|
#ifdef LL_DARWIN
|
|
"accel-mac-option"
|
|
#else
|
|
"accel-win-alt"
|
|
#endif
|
|
);
|
|
tooltip += '\n' + LLTrans::getString("tab_tooltip_prev", args);
|
|
tooltip += '\n' + LLTrans::getString("tab_tooltip_next", args);
|
|
btn->setToolTip( tooltip );
|
|
}
|
|
|
|
LLTabTuple* tuple = new LLTabTuple( this, child, btn, textbox );
|
|
insertTuple( tuple, insertion_point );
|
|
|
|
//Don't add button and textbox if tab buttons are invisible(EXT - 576)
|
|
if (!getTabsHidden())
|
|
{
|
|
if (textbox)
|
|
{
|
|
textbox->setSaveToXML(false);
|
|
addChild( textbox, 0 );
|
|
}
|
|
}
|
|
if (btn)
|
|
{
|
|
btn->setSaveToXML(false);
|
|
btn->setClickedCallback(boost::bind(&LLTabContainer::onTabBtn, this, _2, child));
|
|
addChild( btn, 0 );
|
|
}
|
|
if (child)
|
|
{
|
|
addChild(child, 1);
|
|
}
|
|
|
|
sendChildToFront(mPrevArrowBtn);
|
|
sendChildToFront(mNextArrowBtn);
|
|
sendChildToFront(mJumpPrevArrowBtn);
|
|
sendChildToFront(mJumpNextArrowBtn);
|
|
|
|
if( select )
|
|
{
|
|
selectLastTab();
|
|
}
|
|
|
|
updateMaxScrollPos();
|
|
}
|
|
|
|
void LLTabContainer::addPlaceholder(LLPanel* child, const std::string& label)
|
|
{
|
|
addTabPanel(child, label, FALSE, 0, TRUE);
|
|
}
|
|
|
|
void LLTabContainer::removeTabPanel(LLPanel* child)
|
|
{
|
|
if (mIsVertical)
|
|
{
|
|
// Fix-up button sizes
|
|
S32 tab_count = 0;
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
LLRect rect;
|
|
rect.setLeftTopAndSize(TABCNTRV_PAD + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
|
|
(getRect().getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + TABCNTRV_PAD) * (tab_count)),
|
|
mMinTabWidth,
|
|
BTN_HEIGHT);
|
|
if (tuple->mPlaceholderText)
|
|
{
|
|
tuple->mPlaceholderText->setRect(rect);
|
|
}
|
|
else
|
|
{
|
|
tuple->mButton->setRect(rect);
|
|
}
|
|
tab_count++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Adjust the total tab width.
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
if( tuple->mTabPanel == child )
|
|
{
|
|
mTotalTabWidth -= tuple->mButton->getRect().getWidth();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this);
|
|
|
|
// If the tab being deleted is the selected one, select a different tab.
|
|
for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
if( tuple->mTabPanel == child )
|
|
{
|
|
removeChild( tuple->mButton );
|
|
delete tuple->mButton;
|
|
|
|
removeChild( tuple->mTabPanel );
|
|
// delete tuple->mTabPanel;
|
|
|
|
mTabList.erase( iter );
|
|
delete tuple;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// make sure we don't have more locked tabs than we have tabs
|
|
mLockedTabCount = llmin(getTabCount(), mLockedTabCount);
|
|
|
|
if (mCurrentTabIdx >= (S32)mTabList.size())
|
|
{
|
|
mCurrentTabIdx = mTabList.size()-1;
|
|
}
|
|
selectTab(mCurrentTabIdx);
|
|
if (has_focus)
|
|
{
|
|
LLPanel* panelp = getPanelByIndex(mCurrentTabIdx);
|
|
if (panelp)
|
|
{
|
|
panelp->setFocus(TRUE);
|
|
}
|
|
}
|
|
|
|
updateMaxScrollPos();
|
|
}
|
|
|
|
void LLTabContainer::lockTabs(S32 num_tabs)
|
|
{
|
|
// count current tabs or use supplied value and ensure no new tabs get
|
|
// inserted between them
|
|
mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount();
|
|
}
|
|
|
|
void LLTabContainer::unlockTabs()
|
|
{
|
|
mLockedTabCount = 0;
|
|
}
|
|
|
|
void LLTabContainer::enableTabButton(S32 which, BOOL enable)
|
|
{
|
|
if (which >= 0 && which < (S32)mTabList.size())
|
|
{
|
|
mTabList[which]->mButton->setEnabled(enable);
|
|
}
|
|
// Stop the DaD timer as it might run forever
|
|
// enableTabButton() is typically called on refresh and draw when anything changed
|
|
// in the tab container so it's a good time to reset that.
|
|
mDragAndDropDelayTimer.stop();
|
|
}
|
|
|
|
void LLTabContainer::deleteAllTabs()
|
|
{
|
|
// Remove all the tab buttons and delete them. Also, unlink all the child panels.
|
|
for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
|
|
removeChild( tuple->mButton );
|
|
delete tuple->mButton;
|
|
|
|
removeChild( tuple->mTabPanel );
|
|
// delete tuple->mTabPanel;
|
|
}
|
|
|
|
// Actually delete the tuples themselves
|
|
std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
|
|
mTabList.clear();
|
|
|
|
// And there isn't a current tab any more
|
|
mCurrentTabIdx = -1;
|
|
}
|
|
|
|
LLPanel* LLTabContainer::getCurrentPanel()
|
|
{
|
|
if (mCurrentTabIdx >= 0 && mCurrentTabIdx < (S32) mTabList.size())
|
|
{
|
|
return mTabList[mCurrentTabIdx]->mTabPanel;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
S32 LLTabContainer::getCurrentPanelIndex()
|
|
{
|
|
return mCurrentTabIdx;
|
|
}
|
|
|
|
S32 LLTabContainer::getTabCount()
|
|
{
|
|
return mTabList.size();
|
|
}
|
|
|
|
LLPanel* LLTabContainer::getPanelByIndex(S32 index)
|
|
{
|
|
if (index >= 0 && index < (S32)mTabList.size())
|
|
{
|
|
return mTabList[index]->mTabPanel;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
S32 LLTabContainer::getIndexForPanel(LLPanel* panel)
|
|
{
|
|
for (S32 index = 0; index < (S32)mTabList.size(); index++)
|
|
{
|
|
if (mTabList[index]->mTabPanel == panel)
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
S32 LLTabContainer::getPanelIndexByTitle(const std::string& title)
|
|
{
|
|
for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
|
|
{
|
|
if (title == mTabList[index]->mButton->getLabelSelected())
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
LLPanel *LLTabContainer::getPanelByName(const std::string& name)
|
|
{
|
|
for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
|
|
{
|
|
LLPanel *panel = mTabList[index]->mTabPanel;
|
|
if (name == panel->getName())
|
|
{
|
|
return panel;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Change the name of the button for the current tab.
|
|
void LLTabContainer::setCurrentTabName(const std::string& name)
|
|
{
|
|
// Might not have a tab selected
|
|
if (mCurrentTabIdx < 0) return;
|
|
|
|
mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
|
|
mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
|
|
}
|
|
|
|
void LLTabContainer::selectFirstTab()
|
|
{
|
|
selectTab( 0 );
|
|
}
|
|
|
|
|
|
void LLTabContainer::selectLastTab()
|
|
{
|
|
selectTab( mTabList.size()-1 );
|
|
}
|
|
|
|
void LLTabContainer::selectNextTab()
|
|
{
|
|
BOOL tab_has_focus = FALSE;
|
|
if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
|
|
{
|
|
tab_has_focus = TRUE;
|
|
}
|
|
S32 idx = mCurrentTabIdx+1;
|
|
if (idx >= (S32)mTabList.size())
|
|
idx = 0;
|
|
while (!selectTab(idx) && idx != mCurrentTabIdx)
|
|
{
|
|
idx = (idx + 1 ) % (S32)mTabList.size();
|
|
}
|
|
|
|
if (tab_has_focus)
|
|
{
|
|
mTabList[idx]->mButton->setFocus(TRUE);
|
|
}
|
|
}
|
|
|
|
void LLTabContainer::selectPrevTab()
|
|
{
|
|
BOOL tab_has_focus = FALSE;
|
|
if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
|
|
{
|
|
tab_has_focus = TRUE;
|
|
}
|
|
S32 idx = mCurrentTabIdx-1;
|
|
if (idx < 0)
|
|
idx = mTabList.size()-1;
|
|
while (!selectTab(idx) && idx != mCurrentTabIdx)
|
|
{
|
|
idx = idx - 1;
|
|
if (idx < 0)
|
|
idx = mTabList.size()-1;
|
|
}
|
|
if (tab_has_focus)
|
|
{
|
|
mTabList[idx]->mButton->setFocus(TRUE);
|
|
}
|
|
}
|
|
|
|
BOOL LLTabContainer::selectTabPanel(LLPanel* child)
|
|
{
|
|
S32 idx = 0;
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
if( tuple->mTabPanel == child )
|
|
{
|
|
return selectTab( idx );
|
|
}
|
|
idx++;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL LLTabContainer::selectTab(S32 which)
|
|
{
|
|
if (which >= getTabCount() || which < 0)
|
|
return FALSE;
|
|
|
|
//if( gFocusMgr.childHasKeyboardFocus( this ) )
|
|
//{
|
|
// gFocusMgr.setKeyboardFocus( NULL );
|
|
//}
|
|
|
|
LLTabTuple* selected_tuple = getTab(which);
|
|
if (!selected_tuple)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
LLSD cbdata;
|
|
if (selected_tuple->mTabPanel)
|
|
cbdata = selected_tuple->mTabPanel->getName();
|
|
|
|
BOOL res = FALSE;
|
|
if( !mValidateSignal || (*mValidateSignal)( this, cbdata ) )
|
|
{
|
|
res = setTab(which);
|
|
if (res && mCommitSignal)
|
|
{
|
|
(*mCommitSignal)(this, cbdata);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
BOOL LLTabContainer::setTab(S32 which)
|
|
{
|
|
|
|
LLTabTuple* selected_tuple = getTab(which);
|
|
if (!selected_tuple)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL is_visible = FALSE;
|
|
if (selected_tuple->mButton->getEnabled())
|
|
{
|
|
setCurrentPanelIndex(which);
|
|
|
|
S32 i = 0;
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
BOOL is_selected = ( tuple == selected_tuple );
|
|
tuple->mTabPanel->setVisible( is_selected );
|
|
// tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
|
|
tuple->mButton->setToggleState( is_selected );
|
|
// RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
|
|
tuple->mButton->setTabStop( is_selected );
|
|
|
|
if (is_selected)
|
|
{
|
|
// Make sure selected tab is within scroll region
|
|
if (mIsVertical)
|
|
{
|
|
/*S32 num_visible = getTabCount() - getMaxScrollPos();
|
|
if( i >= getScrollPos() && i <= getScrollPos() + num_visible)
|
|
{
|
|
setCurrentPanelIndex(which);
|
|
is_visible = TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_visible = FALSE;
|
|
}*/
|
|
if (getMaxScrollPos() > 0)
|
|
{
|
|
if( i < getScrollPos() )
|
|
{
|
|
setScrollPos(i);
|
|
}
|
|
else
|
|
{
|
|
S32 available_height_with_arrows = getRect().getHeight() - getTopBorderHeight() - (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
|
|
S32 min_scroll_pos = llmax(i-llfloor(((F32)available_height_with_arrows)/((F32)BTN_HEIGHT))+1,0);
|
|
setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i));
|
|
setScrollPos(llmin(getScrollPos(), getMaxScrollPos()));
|
|
}
|
|
is_visible = TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_visible = TRUE;
|
|
}
|
|
}
|
|
else if (getMaxScrollPos() > 0)
|
|
{
|
|
if( i < getScrollPos() )
|
|
{
|
|
setScrollPos(i);
|
|
}
|
|
else
|
|
{
|
|
S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
|
|
S32 running_tab_width = tuple->mButton->getRect().getWidth();
|
|
S32 j = i - 1;
|
|
S32 min_scroll_pos = i;
|
|
if (running_tab_width < available_width_with_arrows)
|
|
{
|
|
while (j >= 0)
|
|
{
|
|
LLTabTuple* other_tuple = getTab(j);
|
|
running_tab_width += other_tuple->mButton->getRect().getWidth();
|
|
if (running_tab_width > available_width_with_arrows)
|
|
{
|
|
break;
|
|
}
|
|
j--;
|
|
}
|
|
min_scroll_pos = j + 1;
|
|
}
|
|
setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i));
|
|
setScrollPos(llmin(getScrollPos(), getMaxScrollPos()));
|
|
}
|
|
is_visible = TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_visible = TRUE;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
/*if (mIsVertical && getCurrentPanelIndex() >= 0)
|
|
{
|
|
LLTabTuple* tuple = getTab(getCurrentPanelIndex());
|
|
tuple->mTabPanel->setVisible( TRUE );
|
|
tuple->mButton->setToggleState( TRUE );
|
|
}*/
|
|
return is_visible;
|
|
}
|
|
|
|
BOOL LLTabContainer::selectTabByName(const std::string& name)
|
|
{
|
|
LLPanel* panel = getPanelByName(name);
|
|
if (!panel)
|
|
{
|
|
LL_WARNS() << "LLTabContainer::selectTabByName("
|
|
<< name << ") failed" << LL_ENDL;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL result = selectTabPanel(panel);
|
|
return result;
|
|
}
|
|
|
|
BOOL LLTabContainer::getTabPanelFlashing(LLPanel *child)
|
|
{
|
|
LLTabTuple* tuple = getTabByPanel(child);
|
|
if( tuple )
|
|
{
|
|
return tuple->mButton->getFlashing();
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state )
|
|
{
|
|
LLTabTuple* tuple = getTabByPanel(child);
|
|
if( tuple )
|
|
{
|
|
tuple->mButton->setFlashing( state );
|
|
}
|
|
}
|
|
|
|
void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
|
|
{
|
|
LLTabTuple* tuple = getTabByPanel(child);
|
|
if( tuple )
|
|
{
|
|
tuple->mButton->setImageOverlay(image_name, LLFontGL::RIGHT, color);
|
|
reshapeTuple(tuple);
|
|
}
|
|
}
|
|
|
|
void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color)
|
|
{
|
|
LLTabTuple* tuple = getTabByPanel(child);
|
|
if( tuple )
|
|
{
|
|
tuple->mButton->setImageOverlay(image_id, LLFontGL::LEFT, color);
|
|
reshapeTuple(tuple);
|
|
}
|
|
}
|
|
|
|
void LLTabContainer::reshapeTuple(LLTabTuple* tuple)
|
|
{
|
|
|
|
if (!mIsVertical)
|
|
{
|
|
const LLFontGL* fontp = LLResMgr::getInstance()->getRes( LLFONT_SANSSERIF_SMALL );
|
|
S32 image_overlay_width = 0;
|
|
image_overlay_width = tuple->mButton->getImageOverlay().notNull() ?
|
|
tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0;
|
|
// remove current width from total tab strip width
|
|
mTotalTabWidth -= tuple->mButton->getRect().getWidth();
|
|
|
|
|
|
|
|
tuple->mPadding = image_overlay_width;
|
|
|
|
tuple->mButton->setRightHPad(6);
|
|
tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth),
|
|
tuple->mButton->getRect().getHeight());
|
|
// add back in button width to total tab strip width
|
|
mTotalTabWidth += tuple->mButton->getRect().getWidth();
|
|
|
|
// tabs have changed size, might need to scroll to see current tab
|
|
updateMaxScrollPos();
|
|
}
|
|
}
|
|
|
|
void LLTabContainer::setTitle(const std::string& title)
|
|
{
|
|
if (mTitleBox)
|
|
{
|
|
mTitleBox->setText( title );
|
|
}
|
|
}
|
|
|
|
const std::string LLTabContainer::getPanelTitle(S32 index)
|
|
{
|
|
if (index >= 0 && index < (S32)mTabList.size())
|
|
{
|
|
LLButton* tab_button = mTabList[index]->mButton;
|
|
return tab_button->getLabelSelected();
|
|
}
|
|
return LLStringUtil::null;
|
|
}
|
|
|
|
void LLTabContainer::setTopBorderHeight(S32 height)
|
|
{
|
|
mTopBorderHeight = height;
|
|
}
|
|
|
|
S32 LLTabContainer::getTopBorderHeight() const
|
|
{
|
|
return mTopBorderHeight;
|
|
}
|
|
|
|
void LLTabContainer::setRightTabBtnOffset(S32 offset)
|
|
{
|
|
mNextArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
|
|
mRightTabBtnOffset = offset;
|
|
updateMaxScrollPos();
|
|
}
|
|
|
|
void LLTabContainer::setPanelTitle(S32 index, const std::string& title)
|
|
{
|
|
if (index >= 0 && index < getTabCount())
|
|
{
|
|
LLTabTuple* tuple = getTab(index);
|
|
LLButton* tab_button = tuple->mButton;
|
|
if (!mIsVertical)
|
|
{
|
|
const LLFontGL* fontp = LLFontGL::getFontSansSerifSmall();
|
|
mTotalTabWidth -= tab_button->getRect().getWidth();
|
|
tab_button->reshape(llclamp(fontp->getWidth(title)
|
|
+ TAB_PADDING
|
|
+ tuple->mPadding,
|
|
mMinTabWidth,
|
|
mMaxTabWidth),
|
|
tab_button->getRect().getHeight());
|
|
mTotalTabWidth += tab_button->getRect().getWidth();
|
|
}
|
|
tab_button->setLabelSelected(title);
|
|
tab_button->setLabelUnselected(title);
|
|
}
|
|
updateMaxScrollPos();
|
|
}
|
|
|
|
|
|
void LLTabContainer::onTabBtn( const LLSD& data, LLPanel* panel )
|
|
{
|
|
LLTabTuple* tuple = getTabByPanel(panel);
|
|
selectTabPanel( panel );
|
|
|
|
if (tuple)
|
|
{
|
|
tuple->mTabPanel->setFocus(TRUE);
|
|
}
|
|
}
|
|
|
|
void LLTabContainer::onNextBtn( const LLSD& data )
|
|
{
|
|
if (!mScrolled)
|
|
{
|
|
scrollNext();
|
|
}
|
|
mScrolled = FALSE;
|
|
}
|
|
|
|
void LLTabContainer::onNextBtnHeld( const LLSD& data )
|
|
{
|
|
if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
|
|
{
|
|
mScrollTimer.reset();
|
|
scrollNext();
|
|
mScrolled = TRUE;
|
|
}
|
|
}
|
|
|
|
void LLTabContainer::onPrevBtn( const LLSD& data )
|
|
{
|
|
if (!mScrolled)
|
|
{
|
|
scrollPrev();
|
|
}
|
|
mScrolled = FALSE;
|
|
}
|
|
|
|
void LLTabContainer::onJumpFirstBtn( const LLSD& data )
|
|
{
|
|
mScrollPos = 0;
|
|
}
|
|
|
|
void LLTabContainer::onJumpLastBtn( const LLSD& data )
|
|
{
|
|
mScrollPos = mMaxScrollPos;
|
|
}
|
|
|
|
void LLTabContainer::onPrevBtnHeld( const LLSD& data )
|
|
{
|
|
if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
|
|
{
|
|
mScrollTimer.reset();
|
|
scrollPrev();
|
|
mScrolled = TRUE;
|
|
}
|
|
}
|
|
|
|
// static
|
|
LLView* LLTabContainer::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
|
|
{
|
|
std::string name("tab_container");
|
|
node->getAttributeString("name", name);
|
|
|
|
// Figure out if we are creating a vertical or horizontal tab container.
|
|
bool is_vertical = false;
|
|
LLTabContainer::TabPosition tab_position = LLTabContainer::TOP;
|
|
if (node->hasAttribute("tab_position"))
|
|
{
|
|
std::string tab_position_string;
|
|
node->getAttributeString("tab_position", tab_position_string);
|
|
LLStringUtil::toLower(tab_position_string);
|
|
|
|
if ("top" == tab_position_string)
|
|
{
|
|
tab_position = LLTabContainer::TOP;
|
|
is_vertical = false;
|
|
}
|
|
else if ("bottom" == tab_position_string)
|
|
{
|
|
tab_position = LLTabContainer::BOTTOM;
|
|
is_vertical = false;
|
|
}
|
|
else if ("left" == tab_position_string)
|
|
{
|
|
is_vertical = true;
|
|
}
|
|
}
|
|
BOOL border = FALSE;
|
|
node->getAttributeBOOL("border", border);
|
|
|
|
LLTabContainer* tab_container = new LLTabContainer(name, LLRect::null, tab_position, border, is_vertical);
|
|
|
|
S32 tab_min_width = tab_container->mMinTabWidth;
|
|
if (node->hasAttribute("tab_width"))
|
|
{
|
|
node->getAttributeS32("tab_width", tab_min_width);
|
|
}
|
|
else if( node->hasAttribute("tab_min_width"))
|
|
{
|
|
node->getAttributeS32("tab_min_width", tab_min_width);
|
|
}
|
|
|
|
S32 tab_max_width = tab_container->mMaxTabWidth;
|
|
if (node->hasAttribute("tab_max_width"))
|
|
{
|
|
node->getAttributeS32("tab_max_width", tab_max_width);
|
|
}
|
|
|
|
tab_container->setMinTabWidth(tab_min_width);
|
|
tab_container->setMaxTabWidth(tab_max_width);
|
|
|
|
BOOL hidden(tab_container->getTabsHidden());
|
|
node->getAttributeBOOL("hide_tabs", hidden);
|
|
tab_container->setTabsHidden(hidden);
|
|
|
|
tab_container->setPanelParameters(node, parent);
|
|
|
|
if (LLFloater::getFloaterHost())
|
|
{
|
|
LLFloater::getFloaterHost()->setTabContainer(tab_container);
|
|
}
|
|
|
|
//parent->addChild(tab_container);
|
|
|
|
// Add all tab panels.
|
|
LLXMLNodePtr child;
|
|
for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
|
|
{
|
|
LLView *control = factory->createCtrlWidget(tab_container, child);
|
|
if (control && control->isPanel())
|
|
{
|
|
LLPanel* panelp = (LLPanel*)control;
|
|
std::string label;
|
|
child->getAttributeString("label", label);
|
|
if (label.empty())
|
|
{
|
|
label = panelp->getLabel();
|
|
}
|
|
BOOL placeholder = FALSE;
|
|
child->getAttributeBOOL("placeholder", placeholder);
|
|
tab_container->addTabPanel(panelp, label, false, 0, placeholder);
|
|
}
|
|
}
|
|
|
|
tab_container->selectFirstTab();
|
|
|
|
tab_container->postBuild();
|
|
|
|
tab_container->initButtons(); // now that we have the correct rect
|
|
|
|
return tab_container;
|
|
}
|
|
|
|
// private
|
|
|
|
void LLTabContainer::initButtons()
|
|
{
|
|
// Hack:
|
|
if (getRect().getHeight() == 0 || mPrevArrowBtn)
|
|
{
|
|
return; // Don't have a rect yet or already got called
|
|
}
|
|
|
|
std::string out_id;
|
|
std::string in_id;
|
|
|
|
if (mIsVertical)
|
|
{
|
|
// Left and right scroll arrows (for when there are too many tabs to show all at once).
|
|
S32 btn_top = getRect().getHeight();
|
|
S32 btn_top_lower = TABCNTRV_ARROW_BTN_SIZE;
|
|
|
|
LLRect up_arrow_btn_rect;
|
|
up_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top, TABCNTRV_ARROW_BTN_SIZE, TABCNTRV_ARROW_BTN_SIZE );
|
|
|
|
LLRect down_arrow_btn_rect;
|
|
down_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top_lower, TABCNTRV_ARROW_BTN_SIZE, TABCNTRV_ARROW_BTN_SIZE );
|
|
|
|
out_id = "UIImgBtnScrollUpOutUUID";
|
|
in_id = "UIImgBtnScrollUpInUUID";
|
|
mPrevArrowBtn = new LLButton(std::string("Up Arrow"), up_arrow_btn_rect,
|
|
out_id, in_id, LLStringUtil::null, boost::bind(&LLTabContainer::onPrevBtn,this,_2));
|
|
mPrevArrowBtn->setFollowsTop();
|
|
mPrevArrowBtn->setFollowsLeft();
|
|
mPrevArrowBtn->setHeldDownCallback(boost::bind(&LLTabContainer::onPrevBtnHeld, this, _2));
|
|
|
|
out_id = "UIImgBtnScrollDownOutUUID";
|
|
in_id = "UIImgBtnScrollDownInUUID";
|
|
mNextArrowBtn = new LLButton(std::string("Down Arrow"), down_arrow_btn_rect,
|
|
out_id, in_id, LLStringUtil::null, boost::bind(&LLTabContainer::onNextBtn,this,_2) );
|
|
mNextArrowBtn->setFollowsBottom();
|
|
mNextArrowBtn->setFollowsLeft();
|
|
mNextArrowBtn->setHeldDownCallback(boost::bind(&LLTabContainer::onNextBtnHeld, this, _2));
|
|
}
|
|
else // Horizontal
|
|
{
|
|
S32 arrow_fudge = 1; // match new art better
|
|
|
|
// tabs on bottom reserve room for resize handle (just in case)
|
|
if (getTabPosition() == BOTTOM)
|
|
{
|
|
mRightTabBtnOffset = RESIZE_HANDLE_WIDTH;
|
|
}
|
|
|
|
// Left and right scroll arrows (for when there are too many tabs to show all at once).
|
|
S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : TABCNTR_ARROW_BTN_SIZE + 1;
|
|
|
|
LLRect left_arrow_btn_rect;
|
|
left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+TABCNTR_ARROW_BTN_SIZE, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
|
|
|
|
LLRect jump_left_arrow_btn_rect;
|
|
jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
|
|
|
|
S32 right_pad = TABCNTR_ARROW_BTN_SIZE + LLPANEL_BORDER_WIDTH + 1;
|
|
|
|
LLRect right_arrow_btn_rect;
|
|
right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad - TABCNTR_ARROW_BTN_SIZE,
|
|
btn_top + arrow_fudge,
|
|
TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
|
|
|
|
|
|
LLRect jump_right_arrow_btn_rect;
|
|
jump_right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad,
|
|
btn_top + arrow_fudge,
|
|
TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
|
|
|
|
out_id = "UIImgBtnJumpLeftOutUUID";
|
|
in_id = "UIImgBtnJumpLeftInUUID";
|
|
mJumpPrevArrowBtn = new LLButton(std::string("Jump Left Arrow"), jump_left_arrow_btn_rect,
|
|
out_id, in_id, LLStringUtil::null,
|
|
boost::bind(&LLTabContainer::onJumpFirstBtn, this, _2), LLFontGL::getFontSansSerif() );
|
|
mJumpPrevArrowBtn->setFollowsLeft();
|
|
|
|
out_id = "UIImgBtnScrollLeftOutUUID";
|
|
in_id = "UIImgBtnScrollLeftInUUID";
|
|
mPrevArrowBtn = new LLButton(std::string("Left Arrow"), left_arrow_btn_rect,
|
|
out_id, in_id, LLStringUtil::null,
|
|
boost::bind(&LLTabContainer::onPrevBtn, this, _2), LLFontGL::getFontSansSerif() );
|
|
mPrevArrowBtn->setHeldDownCallback(boost::bind(&LLTabContainer::onPrevBtnHeld, this, _2));
|
|
mPrevArrowBtn->setFollowsLeft();
|
|
|
|
out_id = "UIImgBtnJumpRightOutUUID";
|
|
in_id = "UIImgBtnJumpRightInUUID";
|
|
mJumpNextArrowBtn = new LLButton(std::string("Jump Right Arrow"), jump_right_arrow_btn_rect,
|
|
out_id, in_id, LLStringUtil::null,
|
|
boost::bind(&LLTabContainer::onJumpLastBtn, this, _2), LLFontGL::getFontSansSerif());
|
|
mJumpNextArrowBtn->setFollowsRight();
|
|
|
|
out_id = "UIImgBtnScrollRightOutUUID";
|
|
in_id = "UIImgBtnScrollRightInUUID";
|
|
mNextArrowBtn = new LLButton(std::string("Right Arrow"), right_arrow_btn_rect,
|
|
out_id, in_id, LLStringUtil::null,
|
|
boost::bind(&LLTabContainer::onNextBtn, this, _2), LLFontGL::getFontSansSerif());
|
|
mNextArrowBtn->setHeldDownCallback(boost::bind(&LLTabContainer::onNextBtnHeld, this, _2));
|
|
mNextArrowBtn->setFollowsRight();
|
|
|
|
if( getTabPosition() == TOP )
|
|
{
|
|
mNextArrowBtn->setFollowsTop();
|
|
mPrevArrowBtn->setFollowsTop();
|
|
mJumpPrevArrowBtn->setFollowsTop();
|
|
mJumpNextArrowBtn->setFollowsTop();
|
|
}
|
|
else
|
|
{
|
|
mNextArrowBtn->setFollowsBottom();
|
|
mPrevArrowBtn->setFollowsBottom();
|
|
mJumpPrevArrowBtn->setFollowsBottom();
|
|
mJumpNextArrowBtn->setFollowsBottom();
|
|
}
|
|
}
|
|
|
|
mPrevArrowBtn->setSaveToXML(false);
|
|
mPrevArrowBtn->setTabStop(FALSE);
|
|
addChild(mPrevArrowBtn);
|
|
|
|
mNextArrowBtn->setSaveToXML(false);
|
|
mNextArrowBtn->setTabStop(FALSE);
|
|
addChild(mNextArrowBtn);
|
|
|
|
if (mJumpPrevArrowBtn)
|
|
{
|
|
mJumpPrevArrowBtn->setSaveToXML(false);
|
|
mJumpPrevArrowBtn->setTabStop(FALSE);
|
|
addChild(mJumpPrevArrowBtn);
|
|
}
|
|
|
|
if (mJumpNextArrowBtn)
|
|
{
|
|
mJumpNextArrowBtn->setSaveToXML(false);
|
|
mJumpNextArrowBtn->setTabStop(FALSE);
|
|
addChild(mJumpNextArrowBtn);
|
|
}
|
|
|
|
// set default tab group to be panel contents
|
|
setDefaultTabGroup(1);
|
|
}
|
|
|
|
LLTabTuple* LLTabContainer::getTabByPanel(LLPanel* child)
|
|
{
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
if( tuple->mTabPanel == child )
|
|
{
|
|
return tuple;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void LLTabContainer::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point)
|
|
{
|
|
switch(insertion_point)
|
|
{
|
|
case START:
|
|
// insert the new tab in the front of the list
|
|
mTabList.insert(mTabList.begin() + mLockedTabCount, tuple);
|
|
break;
|
|
case LEFT_OF_CURRENT:
|
|
// insert the new tab before the current tab (but not before mLockedTabCount)
|
|
{
|
|
tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx);
|
|
mTabList.insert(current_iter, tuple);
|
|
}
|
|
break;
|
|
|
|
case RIGHT_OF_CURRENT:
|
|
// insert the new tab after the current tab (but not before mLockedTabCount)
|
|
{
|
|
tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1);
|
|
mTabList.insert(current_iter, tuple);
|
|
}
|
|
break;
|
|
case END:
|
|
default:
|
|
mTabList.push_back( tuple );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void LLTabContainer::updateMaxScrollPos()
|
|
{
|
|
BOOL no_scroll = TRUE;
|
|
if (mIsVertical)
|
|
{
|
|
S32 tab_total_height = (BTN_HEIGHT + TABCNTRV_PAD) * getTabCount() + TABCNTRV_PAD;
|
|
S32 available_height = getRect().getHeight() - getTopBorderHeight() - 2 * LLPANEL_BORDER_WIDTH;
|
|
if( tab_total_height > available_height )
|
|
{
|
|
S32 available_height_with_arrows = getRect().getHeight() - getTopBorderHeight() - (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
|
|
S32 additional_needed = tab_total_height - available_height_with_arrows;
|
|
setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT) ) );
|
|
no_scroll = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
S32 tab_space = 0;
|
|
S32 available_space = 0;
|
|
tab_space = mTotalTabWidth;
|
|
available_space = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD);
|
|
|
|
if( tab_space > available_space )
|
|
{
|
|
S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
|
|
// subtract off reserved portion on left
|
|
available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH;
|
|
|
|
S32 running_tab_width = 0;
|
|
setMaxScrollPos(getTabCount());
|
|
for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it)
|
|
{
|
|
running_tab_width += (*tab_it)->mButton->getRect().getWidth();
|
|
if (running_tab_width > available_width_with_arrows)
|
|
{
|
|
break;
|
|
}
|
|
setMaxScrollPos(getMaxScrollPos()-1);
|
|
}
|
|
// in case last tab doesn't actually fit on screen, make it the last scrolling position
|
|
setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1));
|
|
no_scroll = FALSE;
|
|
}
|
|
}
|
|
if (no_scroll)
|
|
{
|
|
setMaxScrollPos(0);
|
|
setScrollPos(0);
|
|
}
|
|
if (getScrollPos() > getMaxScrollPos())
|
|
{
|
|
setScrollPos(getMaxScrollPos()); // maybe just enforce this via limits in setScrollPos instead?
|
|
}
|
|
}
|
|
|
|
void LLTabContainer::commitHoveredButton(S32 x, S32 y)
|
|
{
|
|
if (!getTabsHidden() && hasMouseCapture())
|
|
{
|
|
for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
|
|
{
|
|
LLTabTuple* tuple = *iter;
|
|
//tuple->mButton->setVisible( TRUE );
|
|
S32 local_x = x - tuple->mButton->getRect().mLeft;
|
|
S32 local_y = y - tuple->mButton->getRect().mBottom;
|
|
if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
|
|
{
|
|
tuple->mButton->onCommit();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
S32 LLTabContainer::getTotalTabWidth() const
|
|
{
|
|
return mTotalTabWidth;
|
|
}
|
|
|