aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llui/lltabcontainer.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:45:34 -0500
committerJacek Antonelli2008-08-15 23:45:34 -0500
commitcd17687f01420952712a500107e0f93e7ab8d5f8 (patch)
treece48c2b706f2c1176290e39fb555fbdf6648ce01 /linden/indra/llui/lltabcontainer.cpp
parentSecond Life viewer sources 1.19.0.5 (diff)
downloadmeta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.zip
meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.gz
meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.bz2
meta-impy-cd17687f01420952712a500107e0f93e7ab8d5f8.tar.xz
Second Life viewer sources 1.19.1.0
Diffstat (limited to 'linden/indra/llui/lltabcontainer.cpp')
-rw-r--r--linden/indra/llui/lltabcontainer.cpp2397
1 files changed, 1268 insertions, 1129 deletions
diff --git a/linden/indra/llui/lltabcontainer.cpp b/linden/indra/llui/lltabcontainer.cpp
index 0400b50..e632cf1 100644
--- a/linden/indra/llui/lltabcontainer.cpp
+++ b/linden/indra/llui/lltabcontainer.cpp
@@ -1,6 +1,6 @@
1/** 1/**
2 * @file lltabcontainer.cpp 2 * @file lltabcontainer.cpp
3 * @brief LLTabContainerCommon base class 3 * @brief LLTabContainer class
4 * 4 *
5 * $LicenseInfo:firstyear=2001&license=viewergpl$ 5 * $LicenseInfo:firstyear=2001&license=viewergpl$
6 * 6 *
@@ -30,33 +30,24 @@
30 */ 30 */
31 31
32#include "linden_common.h" 32#include "linden_common.h"
33
34#include "lltabcontainer.h" 33#include "lltabcontainer.h"
35
36#include "llfocusmgr.h" 34#include "llfocusmgr.h"
37#include "llfontgl.h"
38#include "llgl.h"
39
40#include "llbutton.h" 35#include "llbutton.h"
41#include "llrect.h" 36#include "llrect.h"
42#include "llpanel.h"
43#include "llresmgr.h" 37#include "llresmgr.h"
44#include "llkeyboard.h"
45#include "llresizehandle.h" 38#include "llresizehandle.h"
46#include "llui.h"
47#include "lltextbox.h" 39#include "lltextbox.h"
48#include "llcontrol.h"
49#include "llcriticaldamp.h" 40#include "llcriticaldamp.h"
50#include "lluictrlfactory.h" 41#include "lluictrlfactory.h"
51
52#include "lltabcontainervertical.h" 42#include "lltabcontainervertical.h"
43#include "llglimmediate.h"
53 44
54#include "llglheaders.h"
55 45
56const F32 SCROLL_STEP_TIME = 0.4f; 46const F32 SCROLL_STEP_TIME = 0.4f;
57const F32 SCROLL_DELAY_TIME = 0.5f; 47const F32 SCROLL_DELAY_TIME = 0.5f;
58const S32 TAB_PADDING = 15; 48const S32 TAB_PADDING = 15;
59const S32 TABCNTR_TAB_MIN_WIDTH = 60; 49const S32 TABCNTR_TAB_MIN_WIDTH = 60;
50const S32 TABCNTR_VERT_TAB_MIN_WIDTH = 100;
60const S32 TABCNTR_TAB_MAX_WIDTH = 150; 51const S32 TABCNTR_TAB_MAX_WIDTH = 150;
61const S32 TABCNTR_TAB_PARTIAL_WIDTH = 12; // When tabs are parially obscured, how much can you still see. 52const S32 TABCNTR_TAB_PARTIAL_WIDTH = 12; // When tabs are parially obscured, how much can you still see.
62const S32 TABCNTR_TAB_HEIGHT = 16; 53const S32 TABCNTR_TAB_HEIGHT = 16;
@@ -64,12 +55,19 @@ const S32 TABCNTR_ARROW_BTN_SIZE = 16;
64const S32 TABCNTR_BUTTON_PANEL_OVERLAP = 1; // how many pixels the tab buttons and tab panels overlap. 55const S32 TABCNTR_BUTTON_PANEL_OVERLAP = 1; // how many pixels the tab buttons and tab panels overlap.
65const S32 TABCNTR_TAB_H_PAD = 4; 56const S32 TABCNTR_TAB_H_PAD = 4;
66 57
58const S32 TABCNTR_CLOSE_BTN_SIZE = 16;
59const S32 TABCNTR_HEADER_HEIGHT = LLPANEL_BORDER_WIDTH + TABCNTR_CLOSE_BTN_SIZE;
60
61const S32 TABCNTRV_CLOSE_BTN_SIZE = 16;
62const S32 TABCNTRV_HEADER_HEIGHT = LLPANEL_BORDER_WIDTH + TABCNTRV_CLOSE_BTN_SIZE;
63//const S32 TABCNTRV_TAB_WIDTH = 100;
64const S32 TABCNTRV_ARROW_BTN_SIZE = 16;
65const S32 TABCNTRV_PAD = 0;
66
67 67
68LLTabContainerCommon::LLTabContainerCommon( 68
69 const LLString& name, const LLRect& rect, 69LLTabContainer::LLTabContainer(const LLString& name, const LLRect& rect, TabPosition pos,
70 TabPosition pos, 70 BOOL bordered, BOOL is_vertical )
71 void(*close_callback)(void*), void* callback_userdata,
72 BOOL bordered )
73 : 71 :
74 LLPanel(name, rect, bordered), 72 LLPanel(name, rect, bordered),
75 mCurrentTabIdx(-1), 73 mCurrentTabIdx(-1),
@@ -78,50 +76,65 @@ LLTabContainerCommon::LLTabContainerCommon(
78 mScrollPos(0), 76 mScrollPos(0),
79 mScrollPosPixels(0), 77 mScrollPosPixels(0),
80 mMaxScrollPos(0), 78 mMaxScrollPos(0),
81 mCloseCallback( close_callback ), 79 mCloseCallback( NULL ),
82 mCallbackUserdata( callback_userdata ), 80 mCallbackUserdata( NULL ),
83 mTitleBox(NULL), 81 mTitleBox(NULL),
84 mTopBorderHeight(LLPANEL_BORDER_WIDTH), 82 mTopBorderHeight(LLPANEL_BORDER_WIDTH),
85 mTabPosition(pos), 83 mTabPosition(pos),
86 mLockedTabCount(0) 84 mLockedTabCount(0),
85 mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
86 mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
87 mPrevArrowBtn(NULL),
88 mNextArrowBtn(NULL),
89 mIsVertical(is_vertical),
90 // Horizontal Specific
91 mJumpPrevArrowBtn(NULL),
92 mJumpNextArrowBtn(NULL),
93 mRightTabBtnOffset(0),
94 mTotalTabWidth(0)
87{ 95{
96 //RN: HACK to support default min width for legacy vertical tab containers
97 if (mIsVertical)
98 {
99 mMinTabWidth = TABCNTR_VERT_TAB_MIN_WIDTH;
100 }
88 setMouseOpaque(FALSE); 101 setMouseOpaque(FALSE);
102 initButtons( );
89 mDragAndDropDelayTimer.stop(); 103 mDragAndDropDelayTimer.stop();
90} 104}
91 105
106LLTabContainer::~LLTabContainer()
107{
108 std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
109}
92 110
93LLTabContainerCommon::LLTabContainerCommon( 111//virtual
94 const LLString& name, 112void LLTabContainer::setValue(const LLSD& value)
95 const LLString& rect_control,
96 TabPosition pos,
97 void(*close_callback)(void*), void* callback_userdata,
98 BOOL bordered )
99 :
100 LLPanel(name, rect_control, bordered),
101 mCurrentTabIdx(-1),
102 mTabsHidden(FALSE),
103 mScrolled(FALSE),
104 mScrollPos(0),
105 mScrollPosPixels(0),
106 mMaxScrollPos(0),
107 mCloseCallback( close_callback ),
108 mCallbackUserdata( callback_userdata ),
109 mTitleBox(NULL),
110 mTopBorderHeight(LLPANEL_BORDER_WIDTH),
111 mTabPosition(pos),
112 mLockedTabCount(0)
113{ 113{
114 setMouseOpaque(FALSE); 114 selectTab((S32) value.asInteger());
115 mDragAndDropDelayTimer.stop();
116} 115}
117 116
117//virtual
118EWidgetType LLTabContainer::getWidgetType() const
119{
120 return WIDGET_TYPE_TAB_CONTAINER;
121}
118 122
119LLTabContainerCommon::~LLTabContainerCommon() 123//virtual
124LLString LLTabContainer::getWidgetTag() const
120{ 125{
121 std::for_each(mTabList.begin(), mTabList.end(), DeletePointer()); 126 return LL_TAB_CONTAINER_COMMON_TAG;
122} 127}
123 128
124LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse) const 129//virtual
130void LLTabContainer::reshape(S32 width, S32 height, BOOL called_from_parent)
131{
132 LLPanel::reshape( width, height, called_from_parent );
133 updateMaxScrollPos();
134}
135
136//virtual
137LLView* LLTabContainer::getChildByName(const LLString& name, BOOL recurse) const
125{ 138{
126 tuple_list_t::const_iterator itor; 139 tuple_list_t::const_iterator itor;
127 for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) 140 for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
@@ -137,7 +150,7 @@ LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse)
137 for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) 150 for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
138 { 151 {
139 LLPanel *panel = (*itor)->mTabPanel; 152 LLPanel *panel = (*itor)->mTabPanel;
140 LLView *child = panel->getChildByName(name, recurse); 153 LLView *child = panel->getChild<LLView>(name, recurse);
141 if (child) 154 if (child)
142 { 155 {
143 return child; 156 return child;
@@ -147,25 +160,753 @@ LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse)
147 return LLView::getChildByName(name, recurse); 160 return LLView::getChildByName(name, recurse);
148} 161}
149 162
150void LLTabContainerCommon::addPlaceholder(LLPanel* child, const LLString& label) 163// virtual
164void LLTabContainer::draw()
151{ 165{
152 addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE); 166 S32 target_pixel_scroll = 0;
167 S32 cur_scroll_pos = mIsVertical ? 0 : getScrollPos();
168 if (cur_scroll_pos > 0)
169 {
170 S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
171 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
172 {
173 if (cur_scroll_pos == 0)
174 {
175 break;
176 }
177 target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
178 cur_scroll_pos--;
179 }
180
181 // Show part of the tab to the left of what is fully visible
182 target_pixel_scroll -= TABCNTR_TAB_PARTIAL_WIDTH;
183 // clamp so that rightmost tab never leaves right side of screen
184 target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
185 }
186
187 setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f)));
188 if( getVisible() )
189 {
190 BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0);
191 if (!mIsVertical)
192 {
193 mJumpPrevArrowBtn->setVisible( has_scroll_arrows );
194 mJumpNextArrowBtn->setVisible( has_scroll_arrows );
195 }
196 mPrevArrowBtn->setVisible( has_scroll_arrows );
197 mNextArrowBtn->setVisible( has_scroll_arrows );
198
199 S32 left = 0, top = 0;
200 if (mIsVertical)
201 {
202 top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? TABCNTRV_ARROW_BTN_SIZE : 0);
203 top += getScrollPosPixels();
204 }
205 else
206 {
207 // Set the leftmost position of the tab buttons.
208 left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (TABCNTR_ARROW_BTN_SIZE * 2) : TABCNTR_TAB_H_PAD);
209 left -= getScrollPosPixels();
210 }
211
212 // Hide all the buttons
213 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
214 {
215 LLTabTuple* tuple = *iter;
216 tuple->mButton->setVisible( FALSE );
217 }
218
219 LLPanel::draw();
220
221 // if tabs are hidden, don't draw them and leave them in the invisible state
222 if (!getTabsHidden())
223 {
224 // Show all the buttons
225 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
226 {
227 LLTabTuple* tuple = *iter;
228 tuple->mButton->setVisible( TRUE );
229 }
230
231 // Draw some of the buttons...
232 LLRect clip_rect = getLocalRect();
233 if (has_scroll_arrows)
234 {
235 // ...but clip them.
236 if (mIsVertical)
237 {
238 clip_rect.mBottom = mNextArrowBtn->getRect().mTop + 3*TABCNTRV_PAD;
239 clip_rect.mTop = mPrevArrowBtn->getRect().mBottom - 3*TABCNTRV_PAD;
240 }
241 else
242 {
243 clip_rect.mLeft = mPrevArrowBtn->getRect().mRight;
244 clip_rect.mRight = mNextArrowBtn->getRect().mLeft;
245 }
246 }
247 LLLocalClipRect clip(clip_rect);
248
249 S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos();
250 S32 idx = 0;
251 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
252 {
253 LLTabTuple* tuple = *iter;
254
255 tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0,
256 top ? top - tuple->mButton->getRect().mTop : 0 );
257 if (top) top -= BTN_HEIGHT + TABCNTRV_PAD;
258 if (left) left += tuple->mButton->getRect().getWidth();
259
260 if (!mIsVertical)
261 {
262 if( idx < getScrollPos() )
263 {
264 if( tuple->mButton->getFlashing() )
265 {
266 mPrevArrowBtn->setFlashing( TRUE );
267 }
268 }
269 else if( max_scroll_visible < idx )
270 {
271 if( tuple->mButton->getFlashing() )
272 {
273 mNextArrowBtn->setFlashing( TRUE );
274 }
275 }
276 }
277 LLUI::pushMatrix();
278 {
279 LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
280 tuple->mButton->draw();
281 }
282 LLUI::popMatrix();
283
284 idx++;
285 }
286
287
288 if( mIsVertical && has_scroll_arrows )
289 {
290 // Redraw the arrows so that they appears on top.
291 gGL.pushMatrix();
292 gGL.translatef((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f);
293 mPrevArrowBtn->draw();
294 gGL.popMatrix();
295
296 gGL.pushMatrix();
297 gGL.translatef((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f);
298 mNextArrowBtn->draw();
299 gGL.popMatrix();
300 }
301 }
302
303 mPrevArrowBtn->setFlashing(FALSE);
304 mNextArrowBtn->setFlashing(FALSE);
305 }
153} 306}
154 307
155void LLTabContainerCommon::lockTabs(S32 num_tabs) 308
309// virtual
310BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
156{ 311{
157 // count current tabs or use supplied value and ensure no new tabs get 312 BOOL handled = FALSE;
158 // inserted between them 313 BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
159 mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount(); 314
315 if (has_scroll_arrows)
316 {
317 if (mJumpPrevArrowBtn&& mJumpPrevArrowBtn->getRect().pointInRect(x, y))
318 {
319 S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
320 S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
321 handled = mJumpPrevArrowBtn->handleMouseDown(local_x, local_y, mask);
322 }
323 else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
324 {
325 S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
326 S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
327 handled = mJumpNextArrowBtn->handleMouseDown(local_x, local_y, mask);
328 }
329 else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
330 {
331 S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
332 S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
333 handled = mPrevArrowBtn->handleMouseDown(local_x, local_y, mask);
334 }
335 else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
336 {
337 S32 local_x = x - mNextArrowBtn->getRect().mLeft;
338 S32 local_y = y - mNextArrowBtn->getRect().mBottom;
339 handled = mNextArrowBtn->handleMouseDown(local_x, local_y, mask);
340 }
341 }
342 if (!handled)
343 {
344 handled = LLPanel::handleMouseDown( x, y, mask );
345 }
346
347 S32 tab_count = getTabCount();
348 if (tab_count > 0)
349 {
350 LLTabTuple* firsttuple = getTab(0);
351 LLRect tab_rect;
352 if (mIsVertical)
353 {
354 tab_rect = LLRect(firsttuple->mButton->getRect().mLeft,
355 has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - TABCNTRV_PAD : mPrevArrowBtn->getRect().mTop,
356 firsttuple->mButton->getRect().mRight,
357 has_scroll_arrows ? mNextArrowBtn->getRect().mTop + TABCNTRV_PAD : mNextArrowBtn->getRect().mBottom );
358 }
359 else
360 {
361 tab_rect = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft,
362 firsttuple->mButton->getRect().mTop,
363 has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight,
364 firsttuple->mButton->getRect().mBottom );
365 }
366 if( tab_rect.pointInRect( x, y ) )
367 {
368 S32 index = getCurrentPanelIndex();
369 index = llclamp(index, 0, tab_count-1);
370 LLButton* tab_button = getTab(index)->mButton;
371 gFocusMgr.setMouseCapture(this);
372 gFocusMgr.setKeyboardFocus(tab_button);
373 }
374 }
375 return handled;
376}
377
378// virtual
379BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
380{
381 BOOL handled = FALSE;
382 BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
383
384 if (has_scroll_arrows)
385 {
386 if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
387 {
388 S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
389 S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
390 handled = mJumpPrevArrowBtn->handleHover(local_x, local_y, mask);
391 }
392 else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
393 {
394 S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
395 S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
396 handled = mJumpNextArrowBtn->handleHover(local_x, local_y, mask);
397 }
398 else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
399 {
400 S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
401 S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
402 handled = mPrevArrowBtn->handleHover(local_x, local_y, mask);
403 }
404 else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
405 {
406 S32 local_x = x - mNextArrowBtn->getRect().mLeft;
407 S32 local_y = y - mNextArrowBtn->getRect().mBottom;
408 handled = mNextArrowBtn->handleHover(local_x, local_y, mask);
409 }
410 }
411 if (!handled)
412 {
413 handled = LLPanel::handleHover(x, y, mask);
414 }
415
416 commitHoveredButton(x, y);
417 return handled;
160} 418}
161 419
162void LLTabContainerCommon::unlockTabs() 420// virtual
421BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
163{ 422{
164 mLockedTabCount = 0; 423 BOOL handled = FALSE;
424 BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
425
426 if (has_scroll_arrows)
427 {
428 if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
429 {
430 S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
431 S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
432 handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
433 }
434 else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
435 {
436 S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
437 S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
438 handled = mJumpNextArrowBtn->handleMouseUp(local_x, local_y, mask);
439 }
440 else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
441 {
442 S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
443 S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
444 handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
445 }
446 else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
447 {
448 S32 local_x = x - mNextArrowBtn->getRect().mLeft;
449 S32 local_y = y - mNextArrowBtn->getRect().mBottom;
450 handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
451 }
452 }
453 if (!handled)
454 {
455 handled = LLPanel::handleMouseUp( x, y, mask );
456 }
457
458 commitHoveredButton(x, y);
459 LLPanel* cur_panel = getCurrentPanel();
460 if (hasMouseCapture())
461 {
462 if (cur_panel)
463 {
464 if (!cur_panel->focusFirstItem(FALSE))
465 {
466 // if nothing in the panel gets focus, make sure the new tab does
467 // otherwise the last tab might keep focus
468 getTab(getCurrentPanelIndex())->mButton->setFocus(TRUE);
469 }
470 }
471 gFocusMgr.setMouseCapture(NULL);
472 }
473 return handled;
474}
475
476// virtual
477BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect )
478{
479 BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect );
480 if (!handled && getTabCount() > 0)
481 {
482 LLTabTuple* firsttuple = getTab(0);
483
484 BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
485 LLRect clip;
486 if (mIsVertical)
487 {
488 clip = LLRect(firsttuple->mButton->getRect().mLeft,
489 has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - TABCNTRV_PAD : mPrevArrowBtn->getRect().mTop,
490 firsttuple->mButton->getRect().mRight,
491 has_scroll_arrows ? mNextArrowBtn->getRect().mTop + TABCNTRV_PAD : mNextArrowBtn->getRect().mBottom );
492 }
493 else
494 {
495 clip = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft,
496 firsttuple->mButton->getRect().mTop,
497 has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight,
498 firsttuple->mButton->getRect().mBottom );
499 }
500
501 if( clip.pointInRect( x, y ) )
502 {
503 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
504 {
505 LLTabTuple* tuple = *iter;
506 tuple->mButton->setVisible( TRUE );
507 S32 local_x = x - tuple->mButton->getRect().mLeft;
508 S32 local_y = y - tuple->mButton->getRect().mBottom;
509 handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect );
510 if( handled )
511 {
512 break;
513 }
514 }
515 }
516
517 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
518 {
519 LLTabTuple* tuple = *iter;
520 tuple->mButton->setVisible( FALSE );
521 }
522 }
523 return handled;
524}
525
526// virtual
527BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
528{
529 if (!getEnabled()) return FALSE;
530
531 if (!gFocusMgr.childHasKeyboardFocus(this)) return FALSE;
532
533 BOOL handled = FALSE;
534 if (key == KEY_LEFT && mask == MASK_ALT)
535 {
536 selectPrevTab();
537 handled = TRUE;
538 }
539 else if (key == KEY_RIGHT && mask == MASK_ALT)
540 {
541 selectNextTab();
542 handled = TRUE;
543 }
544
545 if (handled)
546 {
547 if (getCurrentPanel())
548 {
549 getCurrentPanel()->setFocus(TRUE);
550 }
551 }
552
553 if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
554 {
555 // if child has focus, but not the current panel, focus is on a button
556 if (mIsVertical)
557 {
558 switch(key)
559 {
560 case KEY_UP:
561 selectPrevTab();
562 handled = TRUE;
563 break;
564 case KEY_DOWN:
565 selectNextTab();
566 handled = TRUE;
567 break;
568 case KEY_LEFT:
569 handled = TRUE;
570 break;
571 case KEY_RIGHT:
572 if (getTabPosition() == LEFT && getCurrentPanel())
573 {
574 getCurrentPanel()->setFocus(TRUE);
575 }
576 handled = TRUE;
577 break;
578 default:
579 break;
580 }
581 }
582 else
583 {
584 switch(key)
585 {
586 case KEY_UP:
587 if (getTabPosition() == BOTTOM && getCurrentPanel())
588 {
589 getCurrentPanel()->setFocus(TRUE);
590 }
591 handled = TRUE;
592 break;
593 case KEY_DOWN:
594 if (getTabPosition() == TOP && getCurrentPanel())
595 {
596 getCurrentPanel()->setFocus(TRUE);
597 }
598 handled = TRUE;
599 break;
600 case KEY_LEFT:
601 selectPrevTab();
602 handled = TRUE;
603 break;
604 case KEY_RIGHT:
605 selectNextTab();
606 handled = TRUE;
607 break;
608 default:
609 break;
610 }
611 }
612 }
613 return handled;
614}
615
616// virtual
617LLXMLNodePtr LLTabContainer::getXML(bool save_children) const
618{
619 LLXMLNodePtr node = LLPanel::getXML();
620 node->createChild("tab_position", TRUE)->setStringValue((getTabPosition() == TOP ? "top" : "bottom"));
621 return node;
622}
623
624// virtual
625BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, LLString &tooltip)
626{
627 BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
628
629 if( mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME )
630 {
631 if (has_scroll_arrows)
632 {
633 if (mJumpPrevArrowBtn->getRect().pointInRect(x, y))
634 {
635 S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
636 S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
637 mJumpPrevArrowBtn->handleHover(local_x, local_y, mask);
638 }
639 if (mJumpNextArrowBtn->getRect().pointInRect(x, y))
640 {
641 S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
642 S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
643 mJumpNextArrowBtn->handleHover(local_x, local_y, mask);
644 }
645 if (mPrevArrowBtn->getRect().pointInRect(x, y))
646 {
647 S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
648 S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
649 mPrevArrowBtn->handleHover(local_x, local_y, mask);
650 }
651 else if (mNextArrowBtn->getRect().pointInRect(x, y))
652 {
653 S32 local_x = x - mNextArrowBtn->getRect().mLeft;
654 S32 local_y = y - mNextArrowBtn->getRect().mBottom;
655 mNextArrowBtn->handleHover(local_x, local_y, mask);
656 }
657 }
658
659 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
660 {
661 LLTabTuple* tuple = *iter;
662 tuple->mButton->setVisible( TRUE );
663 S32 local_x = x - tuple->mButton->getRect().mLeft;
664 S32 local_y = y - tuple->mButton->getRect().mBottom;
665 if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
666 {
667 tuple->mButton->onCommit();
668 mDragAndDropDelayTimer.stop();
669 }
670 }
671 }
672
673 return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip);
165} 674}
166 675
167void LLTabContainerCommon::removeTabPanel(LLPanel* child) 676void LLTabContainer::addTabPanel(LLPanel* child,
677 const LLString& label,
678 BOOL select,
679 void (*on_tab_clicked)(void*, bool),
680 void* userdata,
681 S32 indent,
682 BOOL placeholder,
683 eInsertionPoint insertion_point)
168{ 684{
685 if (child->getParent() == this)
686 {
687 // already a child of mine
688 return;
689 }
690 const LLFontGL* font = gResMgr->getRes( mIsVertical ? LLFONT_SANSSERIF : LLFONT_SANSSERIF_SMALL );
691
692 // Store the original label for possible xml export.
693 child->setLabel(label);
694 LLString trimmed_label = label;
695 LLString::trim(trimmed_label);
696
697 S32 button_width = mMinTabWidth;
698 if (!mIsVertical)
699 {
700 button_width = llclamp(font->getWidth(trimmed_label) + TAB_PADDING, mMinTabWidth, mMaxTabWidth);
701 }
702
703 // Tab panel
704 S32 tab_panel_top;
705 S32 tab_panel_bottom;
706 if( getTabPosition() == LLTabContainer::TOP )
707 {
708 S32 tab_height = mIsVertical ? BTN_HEIGHT : TABCNTR_TAB_HEIGHT;
709 tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - TABCNTR_BUTTON_PANEL_OVERLAP);
710 tab_panel_bottom = LLPANEL_BORDER_WIDTH;
711 }
712 else
713 {
714 tab_panel_top = getRect().getHeight() - getTopBorderHeight();
715 tab_panel_bottom = (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP); // Run to the edge, covering up the border
716 }
717
718 LLRect tab_panel_rect;
719 if (mIsVertical)
720 {
721 tab_panel_rect = LLRect(mMinTabWidth + (LLPANEL_BORDER_WIDTH * 2) + TABCNTRV_PAD,
722 getRect().getHeight() - LLPANEL_BORDER_WIDTH,
723 getRect().getWidth() - LLPANEL_BORDER_WIDTH,
724 LLPANEL_BORDER_WIDTH);
725 }
726 else
727 {
728 tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH,
729 tab_panel_top,
730 getRect().getWidth()-LLPANEL_BORDER_WIDTH,
731 tab_panel_bottom );
732 }
733 child->setFollowsAll();
734 child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
735 child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
736 child->setBackgroundVisible( FALSE ); // No need to overdraw
737 // add this child later
738
739 child->setVisible( FALSE ); // Will be made visible when selected
740
741 mTotalTabWidth += button_width;
742
743 // Tab button
744 LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw().
745 LLString tab_img;
746 LLString tab_selected_img;
747 S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel
748
749 if (mIsVertical)
750 {
751 btn_rect.setLeftTopAndSize(TABCNTRV_PAD + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
752 (getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + TABCNTRV_PAD) * getTabCount()),
753 mMinTabWidth,
754 BTN_HEIGHT);
755 }
756 else if( getTabPosition() == LLTabContainer::TOP )
757 {
758 btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
759 tab_img = "tab_top_blue.tga";
760 tab_selected_img = "tab_top_selected_blue.tga";
761 }
762 else
763 {
764 btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
765 tab_img = "tab_bottom_blue.tga";
766 tab_selected_img = "tab_bottom_selected_blue.tga";
767 }
768
769 LLTextBox* textbox = NULL;
770 LLButton* btn = NULL;
771
772 if (placeholder)
773 {
774 btn_rect.translate(0, -LLBUTTON_V_PAD-2);
775 textbox = new LLTextBox(trimmed_label, btn_rect, trimmed_label, font);
776
777 btn = new LLButton("", LLRect(0,0,0,0));
778 }
779 else
780 {
781 if (mIsVertical)
782 {
783 btn = new LLButton("vert tab button",
784 btn_rect,
785 "",
786 "",
787 "",
788 &LLTabContainer::onTabBtn, NULL,
789 font,
790 trimmed_label, trimmed_label);
791 btn->setImages("tab_left.tga", "tab_left_selected.tga");
792 btn->setScaleImage(TRUE);
793 btn->setHAlign(LLFontGL::LEFT);
794 btn->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
795 btn->setTabStop(FALSE);
796 if (indent)
797 {
798 btn->setLeftHPad(indent);
799 }
800 }
801 else
802 {
803 LLString tooltip = trimmed_label;
804 tooltip += "\nAlt-Left arrow for previous tab";
805 tooltip += "\nAlt-Right arrow for next tab";
806
807 btn = new LLButton(LLString(child->getName()) + " tab",
808 btn_rect,
809 "", "", "",
810 &LLTabContainer::onTabBtn, NULL, // set userdata below
811 font,
812 trimmed_label, trimmed_label );
813 btn->setVisible( FALSE );
814 btn->setToolTip( tooltip );
815 btn->setScaleImage(TRUE);
816 btn->setImages(tab_img, tab_selected_img);
817
818 // Try to squeeze in a bit more text
819 btn->setLeftHPad( 4 );
820 btn->setRightHPad( 2 );
821 btn->setHAlign(LLFontGL::LEFT);
822 btn->setTabStop(FALSE);
823 if (indent)
824 {
825 btn->setLeftHPad(indent);
826 }
827
828 if( getTabPosition() == TOP )
829 {
830 btn->setFollowsTop();
831 }
832 else
833 {
834 btn->setFollowsBottom();
835 }
836 }
837 }
838
839 LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata, textbox );
840 insertTuple( tuple, insertion_point );
841
842 if (textbox)
843 {
844 textbox->setSaveToXML(false);
845 addChild( textbox, 0 );
846 }
847 if (btn)
848 {
849 btn->setSaveToXML(false);
850 btn->setCallbackUserData( tuple );
851 addChild( btn, 0 );
852 }
853 if (child)
854 {
855 addChild(child, 1);
856 }
857
858 if( select )
859 {
860 selectLastTab();
861 }
862
863 updateMaxScrollPos();
864}
865
866void LLTabContainer::addPlaceholder(LLPanel* child, const LLString& label)
867{
868 addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE);
869}
870
871void LLTabContainer::removeTabPanel(LLPanel* child)
872{
873 if (mIsVertical)
874 {
875 // Fix-up button sizes
876 S32 tab_count = 0;
877 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
878 {
879 LLTabTuple* tuple = *iter;
880 LLRect rect;
881 rect.setLeftTopAndSize(TABCNTRV_PAD + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
882 (getRect().getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + TABCNTRV_PAD) * (tab_count)),
883 mMinTabWidth,
884 BTN_HEIGHT);
885 if (tuple->mPlaceholderText)
886 {
887 tuple->mPlaceholderText->setRect(rect);
888 }
889 else
890 {
891 tuple->mButton->setRect(rect);
892 }
893 tab_count++;
894 }
895 }
896 else
897 {
898 // Adjust the total tab width.
899 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
900 {
901 LLTabTuple* tuple = *iter;
902 if( tuple->mTabPanel == child )
903 {
904 mTotalTabWidth -= tuple->mButton->getRect().getWidth();
905 break;
906 }
907 }
908 }
909
169 BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this); 910 BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this);
170 911
171 // If the tab being deleted is the selected one, select a different tab. 912 // If the tab being deleted is the selected one, select a different tab.
@@ -207,7 +948,27 @@ void LLTabContainerCommon::removeTabPanel(LLPanel* child)
207 updateMaxScrollPos(); 948 updateMaxScrollPos();
208} 949}
209 950
210void LLTabContainerCommon::deleteAllTabs() 951void LLTabContainer::lockTabs(S32 num_tabs)
952{
953 // count current tabs or use supplied value and ensure no new tabs get
954 // inserted between them
955 mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount();
956}
957
958void LLTabContainer::unlockTabs()
959{
960 mLockedTabCount = 0;
961}
962
963void LLTabContainer::enableTabButton(S32 which, BOOL enable)
964{
965 if (which >= 0 && which < (S32)mTabList.size())
966 {
967 mTabList[which]->mButton->setEnabled(enable);
968 }
969}
970
971void LLTabContainer::deleteAllTabs()
211{ 972{
212 // Remove all the tab buttons and delete them. Also, unlink all the child panels. 973 // Remove all the tab buttons and delete them. Also, unlink all the child panels.
213 for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) 974 for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
@@ -229,56 +990,26 @@ void LLTabContainerCommon::deleteAllTabs()
229 mCurrentTabIdx = -1; 990 mCurrentTabIdx = -1;
230} 991}
231 992
232 993LLPanel* LLTabContainer::getCurrentPanel()
233LLPanel* LLTabContainerCommon::getCurrentPanel()
234{ 994{
235 if (mCurrentTabIdx < 0 || mCurrentTabIdx >= (S32) mTabList.size()) return NULL; 995 if (mCurrentTabIdx >= 0 && mCurrentTabIdx < (S32) mTabList.size())
236
237 return mTabList[mCurrentTabIdx]->mTabPanel;
238}
239
240LLTabContainerCommon::LLTabTuple* LLTabContainerCommon::getTabByPanel(LLPanel* child)
241{
242 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
243 { 996 {
244 LLTabTuple* tuple = *iter; 997 return mTabList[mCurrentTabIdx]->mTabPanel;
245 if( tuple->mTabPanel == child )
246 {
247 return tuple;
248 }
249 } 998 }
250 return NULL; 999 return NULL;
251} 1000}
252 1001
253void LLTabContainerCommon::setTabChangeCallback(LLPanel* tab, void (*on_tab_clicked)(void*, bool)) 1002S32 LLTabContainer::getCurrentPanelIndex()
254{
255 LLTabTuple* tuplep = getTabByPanel(tab);
256 if (tuplep)
257 {
258 tuplep->mOnChangeCallback = on_tab_clicked;
259 }
260}
261
262void LLTabContainerCommon::setTabUserData(LLPanel* tab, void* userdata)
263{
264 LLTabTuple* tuplep = getTabByPanel(tab);
265 if (tuplep)
266 {
267 tuplep->mUserData = userdata;
268 }
269}
270
271S32 LLTabContainerCommon::getCurrentPanelIndex()
272{ 1003{
273 return mCurrentTabIdx; 1004 return mCurrentTabIdx;
274} 1005}
275 1006
276S32 LLTabContainerCommon::getTabCount() 1007S32 LLTabContainer::getTabCount()
277{ 1008{
278 return mTabList.size(); 1009 return mTabList.size();
279} 1010}
280 1011
281LLPanel* LLTabContainerCommon::getPanelByIndex(S32 index) 1012LLPanel* LLTabContainer::getPanelByIndex(S32 index)
282{ 1013{
283 if (index >= 0 && index < (S32)mTabList.size()) 1014 if (index >= 0 && index < (S32)mTabList.size())
284 { 1015 {
@@ -287,7 +1018,7 @@ LLPanel* LLTabContainerCommon::getPanelByIndex(S32 index)
287 return NULL; 1018 return NULL;
288} 1019}
289 1020
290S32 LLTabContainerCommon::getIndexForPanel(LLPanel* panel) 1021S32 LLTabContainer::getIndexForPanel(LLPanel* panel)
291{ 1022{
292 for (S32 index = 0; index < (S32)mTabList.size(); index++) 1023 for (S32 index = 0; index < (S32)mTabList.size(); index++)
293 { 1024 {
@@ -299,7 +1030,7 @@ S32 LLTabContainerCommon::getIndexForPanel(LLPanel* panel)
299 return -1; 1030 return -1;
300} 1031}
301 1032
302S32 LLTabContainerCommon::getPanelIndexByTitle(const LLString& title) 1033S32 LLTabContainer::getPanelIndexByTitle(const LLString& title)
303{ 1034{
304 for (S32 index = 0 ; index < (S32)mTabList.size(); index++) 1035 for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
305 { 1036 {
@@ -311,7 +1042,7 @@ S32 LLTabContainerCommon::getPanelIndexByTitle(const LLString& title)
311 return -1; 1042 return -1;
312} 1043}
313 1044
314LLPanel *LLTabContainerCommon::getPanelByName(const LLString& name) 1045LLPanel *LLTabContainer::getPanelByName(const LLString& name)
315{ 1046{
316 for (S32 index = 0 ; index < (S32)mTabList.size(); index++) 1047 for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
317 { 1048 {
@@ -324,34 +1055,71 @@ LLPanel *LLTabContainerCommon::getPanelByName(const LLString& name)
324 return NULL; 1055 return NULL;
325} 1056}
326 1057
1058// Change the name of the button for the current tab.
1059void LLTabContainer::setCurrentTabName(const LLString& name)
1060{
1061 // Might not have a tab selected
1062 if (mCurrentTabIdx < 0) return;
1063
1064 mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
1065 mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
1066}
327 1067
328void LLTabContainerCommon::scrollNext() 1068void LLTabContainer::selectFirstTab()
329{ 1069{
330 // No wrap 1070 selectTab( 0 );
331 if( mScrollPos < mMaxScrollPos )
332 {
333 mScrollPos++;
334 }
335} 1071}
336 1072
337void LLTabContainerCommon::scrollPrev() 1073
1074void LLTabContainer::selectLastTab()
338{ 1075{
339 // No wrap 1076 selectTab( mTabList.size()-1 );
340 if( mScrollPos > 0 ) 1077}
1078
1079void LLTabContainer::selectNextTab()
1080{
1081 BOOL tab_has_focus = FALSE;
1082 if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
1083 {
1084 tab_has_focus = TRUE;
1085 }
1086 S32 idx = mCurrentTabIdx+1;
1087 if (idx >= (S32)mTabList.size())
1088 idx = 0;
1089 while (!selectTab(idx) && idx != mCurrentTabIdx)
341 { 1090 {
342 mScrollPos--; 1091 idx = (idx + 1 ) % (S32)mTabList.size();
1092 }
1093
1094 if (tab_has_focus)
1095 {
1096 mTabList[idx]->mButton->setFocus(TRUE);
343 } 1097 }
344} 1098}
345 1099
346void LLTabContainerCommon::enableTabButton(S32 which, BOOL enable) 1100void LLTabContainer::selectPrevTab()
347{ 1101{
348 if (which >= 0 && which < (S32)mTabList.size()) 1102 BOOL tab_has_focus = FALSE;
1103 if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
349 { 1104 {
350 mTabList[which]->mButton->setEnabled(enable); 1105 tab_has_focus = TRUE;
351 } 1106 }
352} 1107 S32 idx = mCurrentTabIdx-1;
1108 if (idx < 0)
1109 idx = mTabList.size()-1;
1110 while (!selectTab(idx) && idx != mCurrentTabIdx)
1111 {
1112 idx = idx - 1;
1113 if (idx < 0)
1114 idx = mTabList.size()-1;
1115 }
1116 if (tab_has_focus)
1117 {
1118 mTabList[idx]->mButton->setFocus(TRUE);
1119 }
1120}
353 1121
354BOOL LLTabContainerCommon::selectTabPanel(LLPanel* child) 1122BOOL LLTabContainer::selectTabPanel(LLPanel* child)
355{ 1123{
356 S32 idx = 0; 1124 S32 idx = 0;
357 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) 1125 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
@@ -366,12 +1134,108 @@ BOOL LLTabContainerCommon::selectTabPanel(LLPanel* child)
366 return FALSE; 1134 return FALSE;
367} 1135}
368 1136
369BOOL LLTabContainerCommon::selectTabByName(const LLString& name) 1137BOOL LLTabContainer::selectTab(S32 which)
1138{
1139 if (which >= getTabCount()) return FALSE;
1140 if (which < 0) return FALSE;
1141
1142 //if( gFocusMgr.childHasKeyboardFocus( this ) )
1143 //{
1144 // gFocusMgr.setKeyboardFocus( NULL );
1145 //}
1146
1147 LLTabTuple* selected_tuple = getTab(which);
1148 if (!selected_tuple)
1149 {
1150 return FALSE;
1151 }
1152
1153 BOOL is_visible = FALSE;
1154 if (getTab(which)->mButton->getEnabled())
1155 {
1156 setCurrentPanelIndex(which);
1157
1158 S32 i = 0;
1159 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1160 {
1161 LLTabTuple* tuple = *iter;
1162 BOOL is_selected = ( tuple == selected_tuple );
1163 tuple->mTabPanel->setVisible( is_selected );
1164// tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
1165 tuple->mButton->setToggleState( is_selected );
1166 // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
1167 tuple->mButton->setTabStop( is_selected );
1168
1169 if( is_selected && (mIsVertical || (getMaxScrollPos() > 0)))
1170 {
1171 // Make sure selected tab is within scroll region
1172 if (mIsVertical)
1173 {
1174 S32 num_visible = getTabCount() - getMaxScrollPos();
1175 if( i >= getScrollPos() && i <= getScrollPos() + num_visible)
1176 {
1177 setCurrentPanelIndex(which);
1178 is_visible = TRUE;
1179 }
1180 else
1181 {
1182 is_visible = FALSE;
1183 }
1184 }
1185 else
1186 {
1187 if( i < getScrollPos() )
1188 {
1189 setScrollPos(i);
1190 }
1191 else
1192 {
1193 S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
1194 S32 running_tab_width = tuple->mButton->getRect().getWidth();
1195 S32 j = i - 1;
1196 S32 min_scroll_pos = i;
1197 if (running_tab_width < available_width_with_arrows)
1198 {
1199 while (j >= 0)
1200 {
1201 LLTabTuple* other_tuple = getTab(j);
1202 running_tab_width += other_tuple->mButton->getRect().getWidth();
1203 if (running_tab_width > available_width_with_arrows)
1204 {
1205 break;
1206 }
1207 j--;
1208 }
1209 min_scroll_pos = j + 1;
1210 }
1211 setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i));
1212 setScrollPos(llmin(getScrollPos(), getMaxScrollPos()));
1213 }
1214 is_visible = TRUE;
1215 }
1216 }
1217 i++;
1218 }
1219 if( selected_tuple->mOnChangeCallback )
1220 {
1221 selected_tuple->mOnChangeCallback( selected_tuple->mUserData, false );
1222 }
1223 }
1224 if (mIsVertical && getCurrentPanelIndex() >= 0)
1225 {
1226 LLTabTuple* tuple = getTab(getCurrentPanelIndex());
1227 tuple->mTabPanel->setVisible( TRUE );
1228 tuple->mButton->setToggleState( TRUE );
1229 }
1230 return is_visible;
1231}
1232
1233BOOL LLTabContainer::selectTabByName(const LLString& name)
370{ 1234{
371 LLPanel* panel = getPanelByName(name); 1235 LLPanel* panel = getPanelByName(name);
372 if (!panel) 1236 if (!panel)
373 { 1237 {
374 llwarns << "LLTabContainerCommon::selectTabByName(" 1238 llwarns << "LLTabContainer::selectTabByName("
375 << name << ") failed" << llendl; 1239 << name << ") failed" << llendl;
376 return FALSE; 1240 return FALSE;
377 } 1241 }
@@ -380,74 +1244,131 @@ BOOL LLTabContainerCommon::selectTabByName(const LLString& name)
380 return result; 1244 return result;
381} 1245}
382 1246
383 1247BOOL LLTabContainer::getTabPanelFlashing(LLPanel *child)
384void LLTabContainerCommon::selectFirstTab()
385{ 1248{
386 selectTab( 0 ); 1249 LLTabTuple* tuple = getTabByPanel(child);
1250 if( tuple )
1251 {
1252 return tuple->mButton->getFlashing();
1253 }
1254 return FALSE;
387} 1255}
388 1256
389 1257void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state )
390void LLTabContainerCommon::selectLastTab()
391{ 1258{
392 selectTab( mTabList.size()-1 ); 1259 LLTabTuple* tuple = getTabByPanel(child);
1260 if( tuple )
1261 {
1262 tuple->mButton->setFlashing( state );
1263 }
393} 1264}
394 1265
395 1266void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
396void LLTabContainerCommon::selectNextTab()
397{ 1267{
398 BOOL tab_has_focus = FALSE; 1268 LLTabTuple* tuple = getTabByPanel(child);
399 if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus()) 1269 if( tuple )
400 {
401 tab_has_focus = TRUE;
402 }
403 S32 idx = mCurrentTabIdx+1;
404 if (idx >= (S32)mTabList.size())
405 idx = 0;
406 while (!selectTab(idx) && idx != mCurrentTabIdx)
407 { 1270 {
408 idx = (idx + 1 ) % (S32)mTabList.size(); 1271 tuple->mButton->setImageOverlay(image_name, LLFontGL::RIGHT, color);
1272
1273 if (!mIsVertical)
1274 {
1275 const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
1276 // remove current width from total tab strip width
1277 mTotalTabWidth -= tuple->mButton->getRect().getWidth();
1278
1279 S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ?
1280 tuple->mButton->getImageOverlay()->getImage()->getWidth(0) :
1281 0;
1282
1283 tuple->mPadding = image_overlay_width;
1284
1285 tuple->mButton->setRightHPad(6);
1286 tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth),
1287 tuple->mButton->getRect().getHeight());
1288 // add back in button width to total tab strip width
1289 mTotalTabWidth += tuple->mButton->getRect().getWidth();
1290
1291 // tabs have changed size, might need to scroll to see current tab
1292 updateMaxScrollPos();
1293 }
409 } 1294 }
1295}
410 1296
411 if (tab_has_focus) 1297void LLTabContainer::setTitle(const LLString& title)
1298{
1299 if (mTitleBox)
412 { 1300 {
413 mTabList[idx]->mButton->setFocus(TRUE); 1301 mTitleBox->setText( title );
414 } 1302 }
415} 1303}
416 1304
417void LLTabContainerCommon::selectPrevTab() 1305const LLString LLTabContainer::getPanelTitle(S32 index)
418{ 1306{
419 BOOL tab_has_focus = FALSE; 1307 if (index >= 0 && index < (S32)mTabList.size())
420 if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
421 { 1308 {
422 tab_has_focus = TRUE; 1309 LLButton* tab_button = mTabList[index]->mButton;
1310 return tab_button->getLabelSelected();
423 } 1311 }
424 S32 idx = mCurrentTabIdx-1; 1312 return LLString::null;
425 if (idx < 0) 1313}
426 idx = mTabList.size()-1; 1314
427 while (!selectTab(idx) && idx != mCurrentTabIdx) 1315void LLTabContainer::setTopBorderHeight(S32 height)
1316{
1317 mTopBorderHeight = height;
1318}
1319
1320S32 LLTabContainer::getTopBorderHeight() const
1321{
1322 return mTopBorderHeight;
1323}
1324
1325void LLTabContainer::setTabChangeCallback(LLPanel* tab, void (*on_tab_clicked)(void*, bool))
1326{
1327 LLTabTuple* tuplep = getTabByPanel(tab);
1328 if (tuplep)
428 { 1329 {
429 idx = idx - 1; 1330 tuplep->mOnChangeCallback = on_tab_clicked;
430 if (idx < 0)
431 idx = mTabList.size()-1;
432 } 1331 }
433 if (tab_has_focus) 1332}
1333
1334void LLTabContainer::setTabUserData(LLPanel* tab, void* userdata)
1335{
1336 LLTabTuple* tuplep = getTabByPanel(tab);
1337 if (tuplep)
434 { 1338 {
435 mTabList[idx]->mButton->setFocus(TRUE); 1339 tuplep->mUserData = userdata;
436 } 1340 }
437} 1341}
438 1342
1343void LLTabContainer::setRightTabBtnOffset(S32 offset)
1344{
1345 mNextArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
1346 mRightTabBtnOffset = offset;
1347 updateMaxScrollPos();
1348}
439 1349
440void LLTabContainerCommon::reshape(S32 width, S32 height, BOOL called_from_parent) 1350void LLTabContainer::setPanelTitle(S32 index, const LLString& title)
441{ 1351{
442 LLPanel::reshape( width, height, called_from_parent ); 1352 if (index >= 0 && index < getTabCount())
1353 {
1354 LLTabTuple* tuple = getTab(index);
1355 LLButton* tab_button = tuple->mButton;
1356 const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
1357 mTotalTabWidth -= tab_button->getRect().getWidth();
1358 tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
1359 mTotalTabWidth += tab_button->getRect().getWidth();
1360 tab_button->setLabelSelected(title);
1361 tab_button->setLabelUnselected(title);
1362 }
443 updateMaxScrollPos(); 1363 updateMaxScrollPos();
444} 1364}
445 1365
1366
446// static 1367// static
447void LLTabContainerCommon::onTabBtn( void* userdata ) 1368void LLTabContainer::onTabBtn( void* userdata )
448{ 1369{
449 LLTabTuple* tuple = (LLTabTuple*) userdata; 1370 LLTabTuple* tuple = (LLTabTuple*) userdata;
450 LLTabContainerCommon* self = tuple->mTabContainer; 1371 LLTabContainer* self = tuple->mTabContainer;
451 self->selectTabPanel( tuple->mTabPanel ); 1372 self->selectTabPanel( tuple->mTabPanel );
452 1373
453 if( tuple->mOnChangeCallback ) 1374 if( tuple->mOnChangeCallback )
@@ -459,7 +1380,7 @@ void LLTabContainerCommon::onTabBtn( void* userdata )
459} 1380}
460 1381
461// static 1382// static
462void LLTabContainerCommon::onCloseBtn( void* userdata ) 1383void LLTabContainer::onCloseBtn( void* userdata )
463{ 1384{
464 LLTabContainer* self = (LLTabContainer*) userdata; 1385 LLTabContainer* self = (LLTabContainer*) userdata;
465 if( self->mCloseCallback ) 1386 if( self->mCloseCallback )
@@ -469,7 +1390,7 @@ void LLTabContainerCommon::onCloseBtn( void* userdata )
469} 1390}
470 1391
471// static 1392// static
472void LLTabContainerCommon::onNextBtn( void* userdata ) 1393void LLTabContainer::onNextBtn( void* userdata )
473{ 1394{
474 // Scroll tabs to the left 1395 // Scroll tabs to the left
475 LLTabContainer* self = (LLTabContainer*) userdata; 1396 LLTabContainer* self = (LLTabContainer*) userdata;
@@ -481,7 +1402,7 @@ void LLTabContainerCommon::onNextBtn( void* userdata )
481} 1402}
482 1403
483// static 1404// static
484void LLTabContainerCommon::onNextBtnHeld( void* userdata ) 1405void LLTabContainer::onNextBtnHeld( void* userdata )
485{ 1406{
486 LLTabContainer* self = (LLTabContainer*) userdata; 1407 LLTabContainer* self = (LLTabContainer*) userdata;
487 if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) 1408 if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
@@ -493,7 +1414,7 @@ void LLTabContainerCommon::onNextBtnHeld( void* userdata )
493} 1414}
494 1415
495// static 1416// static
496void LLTabContainerCommon::onPrevBtn( void* userdata ) 1417void LLTabContainer::onPrevBtn( void* userdata )
497{ 1418{
498 LLTabContainer* self = (LLTabContainer*) userdata; 1419 LLTabContainer* self = (LLTabContainer*) userdata;
499 if (!self->mScrolled) 1420 if (!self->mScrolled)
@@ -503,26 +1424,22 @@ void LLTabContainerCommon::onPrevBtn( void* userdata )
503 self->mScrolled = FALSE; 1424 self->mScrolled = FALSE;
504} 1425}
505 1426
506 1427// static
507void LLTabContainerCommon::onJumpFirstBtn( void* userdata ) 1428void LLTabContainer::onJumpFirstBtn( void* userdata )
508{ 1429{
509 LLTabContainer* self = (LLTabContainer*) userdata; 1430 LLTabContainer* self = (LLTabContainer*) userdata;
510
511 self->mScrollPos = 0; 1431 self->mScrollPos = 0;
512
513} 1432}
514 1433
515 1434// static
516void LLTabContainerCommon::onJumpLastBtn( void* userdata ) 1435void LLTabContainer::onJumpLastBtn( void* userdata )
517{ 1436{
518 LLTabContainer* self = (LLTabContainer*) userdata; 1437 LLTabContainer* self = (LLTabContainer*) userdata;
519
520 self->mScrollPos = self->mMaxScrollPos; 1438 self->mScrollPos = self->mMaxScrollPos;
521} 1439}
522 1440
523
524// static 1441// static
525void LLTabContainerCommon::onPrevBtnHeld( void* userdata ) 1442void LLTabContainer::onPrevBtnHeld( void* userdata )
526{ 1443{
527 LLTabContainer* self = (LLTabContainer*) userdata; 1444 LLTabContainer* self = (LLTabContainer*) userdata;
528 if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) 1445 if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
@@ -533,68 +1450,8 @@ void LLTabContainerCommon::onPrevBtnHeld( void* userdata )
533 } 1450 }
534} 1451}
535 1452
536BOOL LLTabContainerCommon::getTabPanelFlashing(LLPanel *child) 1453// static
537{ 1454LLView* LLTabContainer::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
538 LLTabTuple* tuple = getTabByPanel(child);
539 if( tuple )
540 {
541 return tuple->mButton->getFlashing();
542 }
543 return FALSE;
544}
545
546void LLTabContainerCommon::setTabPanelFlashing(LLPanel* child, BOOL state )
547{
548 LLTabTuple* tuple = getTabByPanel(child);
549 if( tuple )
550 {
551 tuple->mButton->setFlashing( state );
552 }
553}
554
555void LLTabContainerCommon::setTabImage(LLPanel* child, std::string img_name, const LLColor4& color)
556{
557 LLTabTuple* tuple = getTabByPanel(child);
558 if( tuple )
559 {
560 tuple->mButton->setImageOverlay(img_name, LLFontGL::RIGHT, color);
561 }
562}
563
564void LLTabContainerCommon::setTitle(const LLString& title)
565{
566 if (mTitleBox)
567 {
568 mTitleBox->setText( title );
569 }
570}
571
572const LLString LLTabContainerCommon::getPanelTitle(S32 index)
573{
574 if (index >= 0 && index < (S32)mTabList.size())
575 {
576 LLButton* tab_button = mTabList[index]->mButton;
577 return tab_button->getLabelSelected();
578 }
579 return LLString::null;
580}
581
582void LLTabContainerCommon::setTopBorderHeight(S32 height)
583{
584 mTopBorderHeight = height;
585}
586
587// Change the name of the button for the current tab.
588void LLTabContainerCommon::setCurrentTabName(const LLString& name)
589{
590 // Might not have a tab selected
591 if (mCurrentTabIdx < 0) return;
592
593 mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
594 mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
595}
596
597LLView* LLTabContainerCommon::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
598{ 1455{
599 LLString name("tab_container"); 1456 LLString name("tab_container");
600 node->getAttributeString("name", name); 1457 node->getAttributeString("name", name);
@@ -626,58 +1483,30 @@ LLView* LLTabContainerCommon::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtr
626 BOOL border = FALSE; 1483 BOOL border = FALSE;
627 node->getAttributeBOOL("border", border); 1484 node->getAttributeBOOL("border", border);
628 1485
629 // Create the correct container type. 1486 LLTabContainer* tab_container = new LLTabContainer(name, LLRect::null, tab_position, border, is_vertical);
630 LLTabContainerCommon* tab_container = NULL; 1487
631 1488 S32 tab_min_width = tab_container->mMinTabWidth;
632 if (is_vertical) 1489 if (node->hasAttribute("tab_width"))
633 { 1490 {
634 // Vertical tabs can specify tab width 1491 node->getAttributeS32("tab_width", tab_min_width);
635 U32 tab_width = TABCNTRV_TAB_WIDTH;
636 if (node->hasAttribute("tab_width"))
637 {
638 node->getAttributeU32("tab_width", tab_width);
639 }
640
641 tab_container = new LLTabContainerVertical(name,
642 LLRect::null,
643 NULL,
644 NULL,
645 tab_width,
646 border);
647
648 } 1492 }
649 else // horizontal tab container 1493 else if( node->hasAttribute("tab_min_width"))
650 { 1494 {
651 // Horizontal tabs can have a title (?) 1495 node->getAttributeS32("tab_min_width", tab_min_width);
652 LLString title(LLString::null); 1496 }
653 if (node->hasAttribute("title"))
654 {
655 node->getAttributeString("title", title);
656 }
657 1497
658 tab_container = new LLTabContainer(name, 1498 S32 tab_max_width = tab_container->mMaxTabWidth;
659 LLRect::null, 1499 if (node->hasAttribute("tab_max_width"))
660 tab_position, 1500 {
661 NULL, 1501 node->getAttributeS32("tab_max_width", tab_max_width);
662 NULL,
663 title,
664 border);
665
666 if(node->hasAttribute("tab_min_width"))
667 {
668 S32 minTabWidth=0;
669 node->getAttributeS32("tab_min_width",minTabWidth);
670 ((LLTabContainer*)tab_container)->setMinTabWidth(minTabWidth);
671 }
672 if(node->hasAttribute("tab_max_width"))
673 {
674 S32 maxTabWidth=0;
675 node->getAttributeS32("tab_max_width",maxTabWidth);
676 ((LLTabContainer*)tab_container)->setMaxTabWidth(maxTabWidth);
677 }
678 } 1502 }
1503
1504 tab_container->setMinTabWidth(tab_min_width);
1505 tab_container->setMaxTabWidth(tab_max_width);
679 1506
680 node->getAttributeBOOL("hide_tabs", tab_container->mTabsHidden); 1507 BOOL hidden(tab_container->getTabsHidden());
1508 node->getAttributeBOOL("hide_tabs", hidden);
1509 tab_container->setTabsHidden(hidden);
681 1510
682 tab_container->setPanelParameters(node, parent); 1511 tab_container->setPanelParameters(node, parent);
683 1512
@@ -718,76 +1547,12 @@ LLView* LLTabContainerCommon::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtr
718 return tab_container; 1547 return tab_container;
719} 1548}
720 1549
721void LLTabContainerCommon::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point) 1550// private
722{
723 switch(insertion_point)
724 {
725 case START:
726 // insert the new tab in the front of the list
727 mTabList.insert(mTabList.begin() + mLockedTabCount, tuple);
728 break;
729 case LEFT_OF_CURRENT:
730 // insert the new tab before the current tab (but not before mLockedTabCount)
731 {
732 tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx);
733 mTabList.insert(current_iter, tuple);
734 }
735 break;
736
737 case RIGHT_OF_CURRENT:
738 // insert the new tab after the current tab (but not before mLockedTabCount)
739 {
740 tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1);
741 mTabList.insert(current_iter, tuple);
742 }
743 break;
744 case END:
745 default:
746 mTabList.push_back( tuple );
747 }
748}
749
750
751LLTabContainer::LLTabContainer(
752 const LLString& name, const LLRect& rect, TabPosition pos,
753 void(*close_callback)(void*), void* callback_userdata,
754 const LLString& title, BOOL bordered )
755 :
756 LLTabContainerCommon(name, rect, pos, close_callback, callback_userdata, bordered),
757 mLeftArrowBtn(NULL),
758 mJumpLeftArrowBtn(NULL),
759 mRightArrowBtn(NULL),
760 mJumpRightArrowBtn(NULL),
761 mRightTabBtnOffset(0),
762 mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
763 mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
764 mTotalTabWidth(0)
765{
766 initButtons( );
767}
768
769LLTabContainer::LLTabContainer(
770 const LLString& name, const LLString& rect_control, TabPosition pos,
771 void(*close_callback)(void*), void* callback_userdata,
772 const LLString& title, BOOL bordered )
773 :
774 LLTabContainerCommon(name, rect_control, pos, close_callback, callback_userdata, bordered),
775 mLeftArrowBtn(NULL),
776 mJumpLeftArrowBtn(NULL),
777 mRightArrowBtn(NULL),
778 mJumpRightArrowBtn(NULL),
779 mRightTabBtnOffset(0),
780 mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
781 mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
782 mTotalTabWidth(0)
783{
784 initButtons( );
785}
786 1551
787void LLTabContainer::initButtons() 1552void LLTabContainer::initButtons()
788{ 1553{
789 // Hack: 1554 // Hack:
790 if (mRect.getHeight() == 0 || mLeftArrowBtn) 1555 if (getRect().getHeight() == 0 || mPrevArrowBtn)
791 { 1556 {
792 return; // Don't have a rect yet or already got called 1557 return; // Don't have a rect yet or already got called
793 } 1558 }
@@ -795,881 +1560,255 @@ void LLTabContainer::initButtons()
795 LLString out_id; 1560 LLString out_id;
796 LLString in_id; 1561 LLString in_id;
797 1562
798 S32 arrow_fudge = 1; // match new art better 1563 if (mIsVertical)
799
800 // tabs on bottom reserve room for resize handle (just in case)
801 if (mTabPosition == BOTTOM)
802 { 1564 {
803 mRightTabBtnOffset = RESIZE_HANDLE_WIDTH; 1565 // Left and right scroll arrows (for when there are too many tabs to show all at once).
804 } 1566 S32 btn_top = getRect().getHeight();
805 1567 S32 btn_top_lower = getRect().mBottom+TABCNTRV_ARROW_BTN_SIZE;
806 // Left and right scroll arrows (for when there are too many tabs to show all at once).
807 S32 btn_top = (mTabPosition == TOP ) ? mRect.getHeight() - mTopBorderHeight : TABCNTR_ARROW_BTN_SIZE + 1;
808
809 LLRect left_arrow_btn_rect;
810 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 );
811
812 LLRect jump_left_arrow_btn_rect;
813 jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
814
815 S32 right_pad = TABCNTR_ARROW_BTN_SIZE + LLPANEL_BORDER_WIDTH + 1;
816
817 LLRect right_arrow_btn_rect;
818 right_arrow_btn_rect.setLeftTopAndSize( mRect.getWidth() - mRightTabBtnOffset - right_pad - TABCNTR_ARROW_BTN_SIZE,
819 btn_top + arrow_fudge,
820 TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
821 1568
1569 LLRect up_arrow_btn_rect;
1570 up_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top, TABCNTRV_ARROW_BTN_SIZE, TABCNTRV_ARROW_BTN_SIZE );
822 1571
823 LLRect jump_right_arrow_btn_rect; 1572 LLRect down_arrow_btn_rect;
824 jump_right_arrow_btn_rect.setLeftTopAndSize( mRect.getWidth() - mRightTabBtnOffset - right_pad, 1573 down_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top_lower, TABCNTRV_ARROW_BTN_SIZE, TABCNTRV_ARROW_BTN_SIZE );
825 btn_top + arrow_fudge,
826 TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
827 1574
828 out_id = "UIImgBtnJumpLeftOutUUID"; 1575 out_id = "UIImgBtnScrollUpOutUUID";
829 in_id = "UIImgBtnJumpLeftInUUID"; 1576 in_id = "UIImgBtnScrollUpInUUID";
830 mJumpLeftArrowBtn = new LLButton( 1577 mPrevArrowBtn = new LLButton("Up Arrow", up_arrow_btn_rect,
831 "Jump Left Arrow", jump_left_arrow_btn_rect, 1578 out_id, in_id, "",
832 out_id, in_id, "", 1579 &onPrevBtn, this, NULL );
833 &LLTabContainer::onJumpFirstBtn, this, LLFontGL::sSansSerif ); 1580 mPrevArrowBtn->setFollowsTop();
834 mJumpLeftArrowBtn->setFollowsLeft(); 1581 mPrevArrowBtn->setFollowsLeft();
835 mJumpLeftArrowBtn->setSaveToXML(false);
836 mJumpLeftArrowBtn->setTabStop(FALSE);
837 addChild(mJumpLeftArrowBtn);
838 1582
839 out_id = "UIImgBtnScrollLeftOutUUID"; 1583 out_id = "UIImgBtnScrollDownOutUUID";
840 in_id = "UIImgBtnScrollLeftInUUID"; 1584 in_id = "UIImgBtnScrollDownInUUID";
841 mLeftArrowBtn = new LLButton( 1585 mNextArrowBtn = new LLButton("Down Arrow", down_arrow_btn_rect,
842 "Left Arrow", left_arrow_btn_rect, 1586 out_id, in_id, "",
843 out_id, in_id, "", 1587 &onNextBtn, this, NULL );
844 &LLTabContainer::onPrevBtn, this, LLFontGL::sSansSerif ); 1588 mNextArrowBtn->setFollowsBottom();
845 mLeftArrowBtn->setHeldDownCallback(onPrevBtnHeld); 1589 mNextArrowBtn->setFollowsLeft();
846 mLeftArrowBtn->setFollowsLeft();
847 mLeftArrowBtn->setSaveToXML(false);
848 mLeftArrowBtn->setTabStop(FALSE);
849 addChild(mLeftArrowBtn);
850
851 out_id = "UIImgBtnJumpRightOutUUID";
852 in_id = "UIImgBtnJumpRightInUUID";
853 mJumpRightArrowBtn = new LLButton(
854 "Jump Right Arrow", jump_right_arrow_btn_rect,
855 out_id, in_id, "",
856 &LLTabContainer::onJumpLastBtn, this,
857 LLFontGL::sSansSerif);
858 mJumpRightArrowBtn->setFollowsRight();
859 mJumpRightArrowBtn->setSaveToXML(false);
860 mJumpRightArrowBtn->setTabStop(FALSE);
861 addChild(mJumpRightArrowBtn);
862
863 out_id = "UIImgBtnScrollRightOutUUID";
864 in_id = "UIImgBtnScrollRightInUUID";
865 mRightArrowBtn = new LLButton(
866 "Right Arrow", right_arrow_btn_rect,
867 out_id, in_id, "",
868 &LLTabContainer::onNextBtn, this,
869 LLFontGL::sSansSerif);
870 mRightArrowBtn->setFollowsRight();
871 mRightArrowBtn->setHeldDownCallback(onNextBtnHeld);
872 mRightArrowBtn->setSaveToXML(false);
873 mRightArrowBtn->setTabStop(FALSE);
874 addChild(mRightArrowBtn);
875
876
877 if( mTabPosition == TOP )
878 {
879 mRightArrowBtn->setFollowsTop();
880 mLeftArrowBtn->setFollowsTop();
881 mJumpLeftArrowBtn->setFollowsTop();
882 mJumpRightArrowBtn->setFollowsTop();
883 } 1590 }
884 else 1591 else // Horizontal
885 { 1592 {
886 mRightArrowBtn->setFollowsBottom(); 1593 S32 arrow_fudge = 1; // match new art better
887 mLeftArrowBtn->setFollowsBottom();
888 mJumpLeftArrowBtn->setFollowsBottom();
889 mJumpRightArrowBtn->setFollowsBottom();
890 }
891
892 // set default tab group to be panel contents
893 mDefaultTabGroup = 1;
894}
895 1594
896LLTabContainer::~LLTabContainer() 1595 // tabs on bottom reserve room for resize handle (just in case)
897{ 1596 if (getTabPosition() == BOTTOM)
898} 1597 {
899 1598 mRightTabBtnOffset = RESIZE_HANDLE_WIDTH;
900void LLTabContainer::addTabPanel(LLPanel* child, 1599 }
901 const LLString& label,
902 BOOL select,
903 void (*on_tab_clicked)(void*, bool),
904 void* userdata,
905 S32 indent,
906 BOOL placeholder,
907 eInsertionPoint insertion_point)
908{
909 if (child->getParent() == this)
910 {
911 // already a child of mine
912 return;
913 }
914 const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
915 1600
916 // Store the original label for possible xml export. 1601 // Left and right scroll arrows (for when there are too many tabs to show all at once).
917 child->setLabel(label); 1602 S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : TABCNTR_ARROW_BTN_SIZE + 1;
918 LLString trimmed_label = label;
919 LLString::trim(trimmed_label);
920 1603
921 S32 button_width = llclamp(font->getWidth(trimmed_label) + TAB_PADDING, mMinTabWidth, mMaxTabWidth); 1604 LLRect left_arrow_btn_rect;
1605 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 );
922 1606
923 // Tab panel 1607 LLRect jump_left_arrow_btn_rect;
924 S32 tab_panel_top; 1608 jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
925 S32 tab_panel_bottom;
926 if( LLTabContainer::TOP == mTabPosition )
927 {
928 tab_panel_top = mRect.getHeight() - mTopBorderHeight - (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP);
929 tab_panel_bottom = LLPANEL_BORDER_WIDTH;
930 }
931 else
932 {
933 tab_panel_top = mRect.getHeight() - mTopBorderHeight;
934 tab_panel_bottom = (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP); // Run to the edge, covering up the border
935 }
936
937 LLRect tab_panel_rect(
938 LLPANEL_BORDER_WIDTH,
939 tab_panel_top,
940 mRect.getWidth()-LLPANEL_BORDER_WIDTH,
941 tab_panel_bottom );
942 1609
943 child->setFollowsAll(); 1610 S32 right_pad = TABCNTR_ARROW_BTN_SIZE + LLPANEL_BORDER_WIDTH + 1;
944 child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
945 child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
946 child->setBackgroundVisible( FALSE ); // No need to overdraw
947 // add this child later
948 1611
949 child->setVisible( FALSE ); // Will be made visible when selected 1612 LLRect right_arrow_btn_rect;
1613 right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad - TABCNTR_ARROW_BTN_SIZE,
1614 btn_top + arrow_fudge,
1615 TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
950 1616
951 mTotalTabWidth += button_width;
952 1617
953 // Tab button 1618 LLRect jump_right_arrow_btn_rect;
954 LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw(). 1619 jump_right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad,
955 LLString tab_img; 1620 btn_top + arrow_fudge,
956 LLString tab_selected_img; 1621 TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
957 S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel
958 1622
959 if( LLTabContainer::TOP == mTabPosition ) 1623 out_id = "UIImgBtnJumpLeftOutUUID";
960 { 1624 in_id = "UIImgBtnJumpLeftInUUID";
961 btn_rect.setLeftTopAndSize( 0, mRect.getHeight() - mTopBorderHeight + tab_fudge, button_width, TABCNTR_TAB_HEIGHT ); 1625 mJumpPrevArrowBtn = new LLButton("Jump Left Arrow", jump_left_arrow_btn_rect,
962 tab_img = "tab_top_blue.tga"; 1626 out_id, in_id, "",
963 tab_selected_img = "tab_top_selected_blue.tga"; 1627 &LLTabContainer::onJumpFirstBtn, this, LLFontGL::sSansSerif );
964 } 1628 mJumpPrevArrowBtn->setFollowsLeft();
965 else
966 {
967 btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
968 tab_img = "tab_bottom_blue.tga";
969 tab_selected_img = "tab_bottom_selected_blue.tga";
970 }
971 1629
972 if (placeholder) 1630 out_id = "UIImgBtnScrollLeftOutUUID";
973 { 1631 in_id = "UIImgBtnScrollLeftInUUID";
974 // *FIX: wont work for horizontal tabs 1632 mPrevArrowBtn = new LLButton("Left Arrow", left_arrow_btn_rect,
975 btn_rect.translate(0, -LLBUTTON_V_PAD-2); 1633 out_id, in_id, "",
976 LLString box_label = trimmed_label; 1634 &LLTabContainer::onPrevBtn, this, LLFontGL::sSansSerif );
977 LLTextBox* text = new LLTextBox(box_label, btn_rect, box_label, font); 1635 mPrevArrowBtn->setHeldDownCallback(onPrevBtnHeld);
978 addChild( text, 0 ); 1636 mPrevArrowBtn->setFollowsLeft();
979 1637
980 LLButton* btn = new LLButton("", LLRect(0,0,0,0)); 1638 out_id = "UIImgBtnJumpRightOutUUID";
981 LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata, text ); 1639 in_id = "UIImgBtnJumpRightInUUID";
982 addChild( btn, 0 ); 1640 mJumpNextArrowBtn = new LLButton("Jump Right Arrow", jump_right_arrow_btn_rect,
983 addChild( child, 1 ); 1641 out_id, in_id, "",
984 insertTuple(tuple, insertion_point); 1642 &LLTabContainer::onJumpLastBtn, this,
985 } 1643 LLFontGL::sSansSerif);
986 else 1644 mJumpNextArrowBtn->setFollowsRight();
987 { 1645
988 LLString tooltip = trimmed_label; 1646 out_id = "UIImgBtnScrollRightOutUUID";
989 tooltip += "\nAlt-Left arrow for previous tab"; 1647 in_id = "UIImgBtnScrollRightInUUID";
990 tooltip += "\nAlt-Right arrow for next tab"; 1648 mNextArrowBtn = new LLButton("Right Arrow", right_arrow_btn_rect,
991 1649 out_id, in_id, "",
992 LLButton* btn = new LLButton( 1650 &LLTabContainer::onNextBtn, this,
993 LLString(child->getName()) + " tab", 1651 LLFontGL::sSansSerif);
994 btn_rect, 1652 mNextArrowBtn->setFollowsRight();
995 "", "", "", 1653
996 &LLTabContainer::onTabBtn, NULL, // set userdata below 1654 if( getTabPosition() == TOP )
997 font,
998 trimmed_label, trimmed_label );
999 btn->setSaveToXML(false);
1000 btn->setVisible( FALSE );
1001 btn->setToolTip( tooltip );
1002 btn->setScaleImage(TRUE);
1003 btn->setImages(tab_img, tab_selected_img);
1004
1005 // Try to squeeze in a bit more text
1006 btn->setLeftHPad( 4 );
1007 btn->setRightHPad( 2 );
1008 btn->setHAlign(LLFontGL::LEFT);
1009 btn->setTabStop(FALSE);
1010 if (indent)
1011 {
1012 btn->setLeftHPad(indent);
1013 }
1014
1015 if( mTabPosition == TOP )
1016 { 1655 {
1017 btn->setFollowsTop(); 1656 mNextArrowBtn->setFollowsTop();
1657 mPrevArrowBtn->setFollowsTop();
1658 mJumpPrevArrowBtn->setFollowsTop();
1659 mJumpNextArrowBtn->setFollowsTop();
1018 } 1660 }
1019 else 1661 else
1020 { 1662 {
1021 btn->setFollowsBottom(); 1663 mNextArrowBtn->setFollowsBottom();
1664 mPrevArrowBtn->setFollowsBottom();
1665 mJumpPrevArrowBtn->setFollowsBottom();
1666 mJumpNextArrowBtn->setFollowsBottom();
1022 } 1667 }
1023
1024 LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata );
1025 btn->setCallbackUserData( tuple );
1026 addChild( btn, 0 );
1027 addChild( child, 1 );
1028 insertTuple(tuple, insertion_point);
1029 } 1668 }
1030 1669
1031 updateMaxScrollPos(); 1670 mPrevArrowBtn->setHeldDownCallback(onPrevBtnHeld);
1671 mPrevArrowBtn->setSaveToXML(false);
1672 mPrevArrowBtn->setTabStop(FALSE);
1673 addChild(mPrevArrowBtn);
1032 1674
1033 if( select ) 1675 mNextArrowBtn->setHeldDownCallback(onNextBtnHeld);
1676 mNextArrowBtn->setSaveToXML(false);
1677 mNextArrowBtn->setTabStop(FALSE);
1678 addChild(mNextArrowBtn);
1679
1680 if (mJumpPrevArrowBtn)
1034 { 1681 {
1035 selectLastTab(); 1682 mJumpPrevArrowBtn->setSaveToXML(false);
1683 mJumpPrevArrowBtn->setTabStop(FALSE);
1684 addChild(mJumpPrevArrowBtn);
1036 } 1685 }
1686
1687 if (mJumpNextArrowBtn)
1688 {
1689 mJumpNextArrowBtn->setSaveToXML(false);
1690 mJumpNextArrowBtn->setTabStop(FALSE);
1691 addChild(mJumpNextArrowBtn);
1692 }
1693
1694 // set default tab group to be panel contents
1695 setDefaultTabGroup(1);
1037} 1696}
1038 1697
1039void LLTabContainer::removeTabPanel(LLPanel* child) 1698LLTabContainer::LLTabTuple* LLTabContainer::getTabByPanel(LLPanel* child)
1040{ 1699{
1041 // Adjust the total tab width.
1042 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) 1700 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1043 { 1701 {
1044 LLTabTuple* tuple = *iter; 1702 LLTabTuple* tuple = *iter;
1045 if( tuple->mTabPanel == child ) 1703 if( tuple->mTabPanel == child )
1046 { 1704 {
1047 mTotalTabWidth -= tuple->mButton->getRect().getWidth(); 1705 return tuple;
1048 break;
1049 } 1706 }
1050 } 1707 }
1051 1708 return NULL;
1052 LLTabContainerCommon::removeTabPanel(child);
1053}
1054
1055void LLTabContainer::setPanelTitle(S32 index, const LLString& title)
1056{
1057 if (index >= 0 && index < (S32)mTabList.size())
1058 {
1059 LLTabTuple* tuple = mTabList[index];
1060 LLButton* tab_button = tuple->mButton;
1061 const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
1062 mTotalTabWidth -= tab_button->getRect().getWidth();
1063 tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
1064 mTotalTabWidth += tab_button->getRect().getWidth();
1065 tab_button->setLabelSelected(title);
1066 tab_button->setLabelUnselected(title);
1067 }
1068 updateMaxScrollPos();
1069} 1709}
1070 1710
1071 1711void LLTabContainer::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point)
1072void LLTabContainer::updateMaxScrollPos()
1073{ 1712{
1074 S32 tab_space = 0; 1713 switch(insertion_point)
1075 S32 available_space = 0;
1076 tab_space = mTotalTabWidth;
1077 available_space = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD);
1078
1079 if( tab_space > available_space )
1080 { 1714 {
1081 S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1); 1715 case START:
1082 // subtract off reserved portion on left 1716 // insert the new tab in the front of the list
1083 available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH; 1717 mTabList.insert(mTabList.begin() + mLockedTabCount, tuple);
1084 1718 break;
1085 S32 running_tab_width = 0; 1719 case LEFT_OF_CURRENT:
1086 mMaxScrollPos = mTabList.size(); 1720 // insert the new tab before the current tab (but not before mLockedTabCount)
1087 for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it)
1088 { 1721 {
1089 running_tab_width += (*tab_it)->mButton->getRect().getWidth(); 1722 tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx);
1090 if (running_tab_width > available_width_with_arrows) 1723 mTabList.insert(current_iter, tuple);
1091 {
1092 break;
1093 }
1094 mMaxScrollPos--;
1095 } 1724 }
1096 // in case last tab doesn't actually fit on screen, make it the last scrolling position 1725 break;
1097 mMaxScrollPos = llmin(mMaxScrollPos, (S32)mTabList.size() - 1);
1098 }
1099 else
1100 {
1101 mMaxScrollPos = 0;
1102 mScrollPos = 0;
1103 }
1104 if (mScrollPos > mMaxScrollPos)
1105 {
1106 mScrollPos = mMaxScrollPos;
1107 }
1108}
1109 1726
1110void LLTabContainer::commitHoveredButton(S32 x, S32 y) 1727 case RIGHT_OF_CURRENT:
1111{ 1728 // insert the new tab after the current tab (but not before mLockedTabCount)
1112 if (hasMouseCapture())
1113 {
1114 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1115 { 1729 {
1116 LLTabTuple* tuple = *iter; 1730 tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1);
1117 tuple->mButton->setVisible( TRUE ); 1731 mTabList.insert(current_iter, tuple);
1118 S32 local_x = x - tuple->mButton->getRect().mLeft;
1119 S32 local_y = y - tuple->mButton->getRect().mBottom;
1120 if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
1121 {
1122 tuple->mButton->onCommit();
1123 }
1124 } 1732 }
1733 break;
1734 case END:
1735 default:
1736 mTabList.push_back( tuple );
1125 } 1737 }
1126} 1738}
1127 1739
1128void LLTabContainer::setMinTabWidth(S32 width)
1129{
1130 mMinTabWidth = width;
1131}
1132
1133void LLTabContainer::setMaxTabWidth(S32 width)
1134{
1135 mMaxTabWidth = width;
1136}
1137
1138S32 LLTabContainer::getMinTabWidth() const
1139{
1140 return mMinTabWidth;
1141}
1142 1740
1143S32 LLTabContainer::getMaxTabWidth() const
1144{
1145 return mMaxTabWidth;
1146}
1147 1741
1148BOOL LLTabContainer::selectTab(S32 which) 1742void LLTabContainer::updateMaxScrollPos()
1149{ 1743{
1150 if (which >= (S32)mTabList.size()) return FALSE; 1744 BOOL no_scroll = TRUE;
1151 if (which < 0) return FALSE; 1745 if (mIsVertical)
1152
1153 //if( gFocusMgr.childHasKeyboardFocus( this ) )
1154 //{
1155 // gFocusMgr.setKeyboardFocus( NULL );
1156 //}
1157
1158 LLTabTuple* selected_tuple = mTabList[which];
1159 if (!selected_tuple)
1160 {
1161 return FALSE;
1162 }
1163
1164 if (mTabList[which]->mButton->getEnabled())
1165 { 1746 {
1166 mCurrentTabIdx = which; 1747 S32 tab_total_height = (BTN_HEIGHT + TABCNTRV_PAD) * getTabCount();
1167 1748 S32 available_height = getRect().getHeight() - getTopBorderHeight();
1168 S32 i = 0; 1749 if( tab_total_height > available_height )
1169 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1170 { 1750 {
1171 LLTabTuple* tuple = *iter; 1751 S32 available_height_with_arrows = getRect().getHeight() - 2*(TABCNTRV_ARROW_BTN_SIZE + 3*TABCNTRV_PAD);
1172 BOOL is_selected = ( tuple == selected_tuple ); 1752 S32 additional_needed = tab_total_height - available_height_with_arrows;
1173 tuple->mTabPanel->setVisible( is_selected ); 1753 setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT) ) );
1174// tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here. 1754 no_scroll = FALSE;
1175 tuple->mButton->setToggleState( is_selected );
1176 // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
1177 tuple->mButton->setTabStop( is_selected );
1178
1179 if( is_selected && mMaxScrollPos > 0)
1180 {
1181 // Make sure selected tab is within scroll region
1182 if( i < mScrollPos )
1183 {
1184 mScrollPos = i;
1185 }
1186 else
1187 {
1188 S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
1189 S32 running_tab_width = tuple->mButton->getRect().getWidth();
1190 S32 j = i - 1;
1191 S32 min_scroll_pos = i;
1192 if (running_tab_width < available_width_with_arrows)
1193 {
1194 while (j >= 0)
1195 {
1196 LLTabTuple* other_tuple = mTabList[j];
1197 running_tab_width += other_tuple->mButton->getRect().getWidth();
1198 if (running_tab_width > available_width_with_arrows)
1199 {
1200 break;
1201 }
1202 j--;
1203 }
1204 min_scroll_pos = j + 1;
1205 }
1206 mScrollPos = llclamp(mScrollPos, min_scroll_pos, i);
1207 mScrollPos = llmin(mScrollPos, mMaxScrollPos);
1208 }
1209 }
1210 i++;
1211 }
1212 if( selected_tuple->mOnChangeCallback )
1213 {
1214 selected_tuple->mOnChangeCallback( selected_tuple->mUserData, false );
1215 } 1755 }
1216 return TRUE;
1217 } 1756 }
1218 else 1757 else
1219 { 1758 {
1220 return FALSE; 1759 S32 tab_space = 0;
1221 } 1760 S32 available_space = 0;
1222} 1761 tab_space = mTotalTabWidth;
1762 available_space = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD);
1223 1763
1224void LLTabContainer::draw() 1764 if( tab_space > available_space )
1225{
1226 S32 target_pixel_scroll = 0;
1227 S32 cur_scroll_pos = mScrollPos;
1228 if (cur_scroll_pos > 0)
1229 {
1230 S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
1231 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1232 { 1765 {
1233 if (cur_scroll_pos == 0) 1766 S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1);
1234 { 1767 // subtract off reserved portion on left
1235 break; 1768 available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH;
1236 }
1237 target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
1238 cur_scroll_pos--;
1239 }
1240 1769
1241 // Show part of the tab to the left of what is fully visible 1770 S32 running_tab_width = 0;
1242 target_pixel_scroll -= TABCNTR_TAB_PARTIAL_WIDTH; 1771 setMaxScrollPos(getTabCount());
1243 // clamp so that rightmost tab never leaves right side of screen 1772 for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it)
1244 target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
1245 }
1246
1247 mScrollPosPixels = (S32)lerp((F32)mScrollPosPixels, (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f));
1248 if( getVisible() )
1249 {
1250 BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0);
1251 mJumpLeftArrowBtn->setVisible( has_scroll_arrows );
1252 mJumpRightArrowBtn->setVisible( has_scroll_arrows );
1253 mLeftArrowBtn->setVisible( has_scroll_arrows );
1254 mRightArrowBtn->setVisible( has_scroll_arrows );
1255
1256 // Set the leftmost position of the tab buttons.
1257 S32 left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (TABCNTR_ARROW_BTN_SIZE * 2) : TABCNTR_TAB_H_PAD);
1258 left -= mScrollPosPixels;
1259
1260 // Hide all the buttons
1261 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1262 {
1263 LLTabTuple* tuple = *iter;
1264 tuple->mButton->setVisible( FALSE );
1265 }
1266
1267 LLPanel::draw();
1268
1269 // if tabs are hidden, don't draw them and leave them in the invisible state
1270 if (!mTabsHidden)
1271 {
1272 // Show all the buttons
1273 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1274 {
1275 LLTabTuple* tuple = *iter;
1276 tuple->mButton->setVisible( TRUE );
1277 }
1278
1279 // Draw some of the buttons...
1280 LLRect clip_rect = getLocalRect();
1281 if (has_scroll_arrows)
1282 { 1773 {
1283 // ...but clip them. 1774 running_tab_width += (*tab_it)->mButton->getRect().getWidth();
1284 clip_rect.mLeft = mLeftArrowBtn->getRect().mRight; 1775 if (running_tab_width > available_width_with_arrows)
1285 clip_rect.mRight = mRightArrowBtn->getRect().mLeft;
1286 }
1287 LLLocalClipRect clip(clip_rect);
1288
1289 S32 max_scroll_visible = mTabList.size() - mMaxScrollPos + mScrollPos;
1290 S32 idx = 0;
1291 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1292 {
1293 LLTabTuple* tuple = *iter;
1294
1295 tuple->mButton->translate( left - tuple->mButton->getRect().mLeft, 0 );
1296 left += tuple->mButton->getRect().getWidth();
1297
1298 if( idx < mScrollPos )
1299 {
1300 if( tuple->mButton->getFlashing() )
1301 {
1302 mLeftArrowBtn->setFlashing( TRUE );
1303 }
1304 }
1305 else
1306 if( max_scroll_visible < idx )
1307 { 1776 {
1308 if( tuple->mButton->getFlashing() ) 1777 break;
1309 {
1310 mRightArrowBtn->setFlashing( TRUE );
1311 }
1312 }
1313
1314 LLUI::pushMatrix();
1315 {
1316 LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
1317 tuple->mButton->draw();
1318 } 1778 }
1319 LLUI::popMatrix(); 1779 setMaxScrollPos(getMaxScrollPos()-1);
1320
1321 idx++;
1322 } 1780 }
1781 // in case last tab doesn't actually fit on screen, make it the last scrolling position
1782 setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1));
1783 no_scroll = FALSE;
1323 } 1784 }
1324
1325 mLeftArrowBtn->setFlashing(FALSE);
1326 mRightArrowBtn->setFlashing(FALSE);
1327 } 1785 }
1328} 1786 if (no_scroll)
1329
1330
1331void LLTabContainer::setRightTabBtnOffset(S32 offset)
1332{
1333 mRightArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
1334 mRightTabBtnOffset = offset;
1335 updateMaxScrollPos();
1336}
1337
1338BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
1339{
1340 BOOL handled = FALSE;
1341 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1342
1343 if (has_scroll_arrows)
1344 {
1345 if (mJumpLeftArrowBtn->getRect().pointInRect(x, y))
1346 {
1347 S32 local_x = x - mJumpLeftArrowBtn->getRect().mLeft;
1348 S32 local_y = y - mJumpLeftArrowBtn->getRect().mBottom;
1349 handled = mJumpLeftArrowBtn->handleMouseDown(local_x, local_y, mask);
1350 }
1351 if (mJumpRightArrowBtn->getRect().pointInRect(x, y))
1352 {
1353 S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft;
1354 S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom;
1355 handled = mJumpRightArrowBtn->handleMouseDown(local_x, local_y, mask);
1356 }
1357 if (mLeftArrowBtn->getRect().pointInRect(x, y))
1358 {
1359 S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
1360 S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
1361 handled = mLeftArrowBtn->handleMouseDown(local_x, local_y, mask);
1362 }
1363 else if (mRightArrowBtn->getRect().pointInRect(x, y))
1364 {
1365 S32 local_x = x - mRightArrowBtn->getRect().mLeft;
1366 S32 local_y = y - mRightArrowBtn->getRect().mBottom;
1367 handled = mRightArrowBtn->handleMouseDown(local_x, local_y, mask);
1368 }
1369 }
1370 if (!handled)
1371 { 1787 {
1372 handled = LLPanel::handleMouseDown( x, y, mask ); 1788 setMaxScrollPos(0);
1373 } 1789 setScrollPos(0);
1374
1375 if (mTabList.size() > 0)
1376 {
1377 LLTabTuple* firsttuple = mTabList[0];
1378 LLRect tab_rect(has_scroll_arrows ? mLeftArrowBtn->getRect().mRight : mJumpLeftArrowBtn->getRect().mLeft,
1379 firsttuple->mButton->getRect().mTop,
1380 has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mJumpRightArrowBtn->getRect().mRight,
1381 firsttuple->mButton->getRect().mBottom );
1382 if( tab_rect.pointInRect( x, y ) )
1383 {
1384 LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton;
1385 gFocusMgr.setMouseCapture(this);
1386 gFocusMgr.setKeyboardFocus(tab_button);
1387 }
1388 }
1389 return handled;
1390}
1391
1392BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
1393{
1394 BOOL handled = FALSE;
1395 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1396
1397 if (has_scroll_arrows)
1398 {
1399 if (mJumpLeftArrowBtn->getRect().pointInRect(x, y))
1400 {
1401 S32 local_x = x - mJumpLeftArrowBtn->getRect().mLeft;
1402 S32 local_y = y - mJumpLeftArrowBtn->getRect().mBottom;
1403 handled = mJumpLeftArrowBtn->handleHover(local_x, local_y, mask);
1404 }
1405 if (mJumpRightArrowBtn->getRect().pointInRect(x, y))
1406 {
1407 S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft;
1408 S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom;
1409 handled = mJumpRightArrowBtn->handleHover(local_x, local_y, mask);
1410 }
1411 if (mLeftArrowBtn->getRect().pointInRect(x, y))
1412 {
1413 S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
1414 S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
1415 handled = mLeftArrowBtn->handleHover(local_x, local_y, mask);
1416 }
1417 else if (mRightArrowBtn->getRect().pointInRect(x, y))
1418 {
1419 S32 local_x = x - mRightArrowBtn->getRect().mLeft;
1420 S32 local_y = y - mRightArrowBtn->getRect().mBottom;
1421 handled = mRightArrowBtn->handleHover(local_x, local_y, mask);
1422 }
1423 } 1790 }
1424 if (!handled) 1791 if (getScrollPos() > getMaxScrollPos())
1425 { 1792 {
1426 handled = LLPanel::handleHover(x, y, mask); 1793 setScrollPos(getMaxScrollPos()); // maybe just enforce this via limits in setScrollPos instead?
1427 } 1794 }
1428
1429 commitHoveredButton(x, y);
1430 return handled;
1431} 1795}
1432 1796
1433BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) 1797void LLTabContainer::commitHoveredButton(S32 x, S32 y)
1434{ 1798{
1435 BOOL handled = FALSE;
1436 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1437
1438 if (has_scroll_arrows)
1439 {
1440 if (mJumpLeftArrowBtn->getRect().pointInRect(x, y))
1441 {
1442 S32 local_x = x - mJumpLeftArrowBtn->getRect().mLeft;
1443 S32 local_y = y - mJumpLeftArrowBtn->getRect().mBottom;
1444 handled = mJumpLeftArrowBtn->handleMouseUp(local_x, local_y, mask);
1445 }
1446 if (mJumpRightArrowBtn->getRect().pointInRect(x, y))
1447 {
1448 S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft;
1449 S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom;
1450 handled = mJumpRightArrowBtn->handleMouseUp(local_x, local_y, mask);
1451 }
1452 if (mLeftArrowBtn->getRect().pointInRect(x, y))
1453 {
1454 S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
1455 S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
1456 handled = mLeftArrowBtn->handleMouseUp(local_x, local_y, mask);
1457 }
1458 else if (mRightArrowBtn->getRect().pointInRect(x, y))
1459 {
1460 S32 local_x = x - mRightArrowBtn->getRect().mLeft;
1461 S32 local_y = y - mRightArrowBtn->getRect().mBottom;
1462 handled = mRightArrowBtn->handleMouseUp(local_x, local_y, mask);
1463 }
1464 }
1465 if (!handled)
1466 {
1467 handled = LLPanel::handleMouseUp( x, y, mask );
1468 }
1469
1470 commitHoveredButton(x, y);
1471 LLPanel* cur_panel = getCurrentPanel();
1472 if (hasMouseCapture()) 1799 if (hasMouseCapture())
1473 { 1800 {
1474 if (cur_panel)
1475 {
1476 if (!cur_panel->focusFirstItem(FALSE))
1477 {
1478 // if nothing in the panel gets focus, make sure the new tab does
1479 // otherwise the last tab might keep focus
1480 mTabList[getCurrentPanelIndex()]->mButton->setFocus(TRUE);
1481 }
1482 }
1483 gFocusMgr.setMouseCapture(NULL);
1484 }
1485 return handled;
1486}
1487
1488BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect )
1489{
1490 BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect );
1491 if (!handled && mTabList.size() > 0)
1492 {
1493 LLTabTuple* firsttuple = mTabList[0];
1494
1495 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1496 LLRect clip(
1497 has_scroll_arrows ? mJumpLeftArrowBtn->getRect().mRight : mJumpLeftArrowBtn->getRect().mLeft,
1498 firsttuple->mButton->getRect().mTop,
1499 has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mRightArrowBtn->getRect().mRight,
1500 0 );
1501 if( clip.pointInRect( x, y ) )
1502 {
1503 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1504 {
1505 LLTabTuple* tuple = *iter;
1506 tuple->mButton->setVisible( TRUE );
1507 S32 local_x = x - tuple->mButton->getRect().mLeft;
1508 S32 local_y = y - tuple->mButton->getRect().mBottom;
1509 handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect );
1510 if( handled )
1511 {
1512 break;
1513 }
1514 }
1515 }
1516
1517 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) 1801 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1518 { 1802 {
1519 LLTabTuple* tuple = *iter; 1803 LLTabTuple* tuple = *iter;
1520 tuple->mButton->setVisible( FALSE ); 1804 tuple->mButton->setVisible( TRUE );
1521 } 1805 S32 local_x = x - tuple->mButton->getRect().mLeft;
1522 } 1806 S32 local_y = y - tuple->mButton->getRect().mBottom;
1523 return handled; 1807 if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
1524}
1525
1526BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
1527{
1528 if (!getEnabled()) return FALSE;
1529
1530 if (!gFocusMgr.childHasKeyboardFocus(this)) return FALSE;
1531
1532 BOOL handled = FALSE;
1533 if (key == KEY_LEFT && mask == MASK_ALT)
1534 {
1535 selectPrevTab();
1536 handled = TRUE;
1537 }
1538 else if (key == KEY_RIGHT && mask == MASK_ALT)
1539 {
1540 selectNextTab();
1541 handled = TRUE;
1542 }
1543
1544 if (handled)
1545 {
1546 if (getCurrentPanel())
1547 {
1548 getCurrentPanel()->setFocus(TRUE);
1549 }
1550 }
1551
1552 if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
1553 {
1554 // if child has focus, but not the current panel, focus
1555 // is on a button
1556 switch(key)
1557 {
1558 case KEY_UP:
1559 if (getTabPosition() == BOTTOM && getCurrentPanel())
1560 {
1561 getCurrentPanel()->setFocus(TRUE);
1562 }
1563 handled = TRUE;
1564 break;
1565 case KEY_DOWN:
1566 if (getTabPosition() == TOP && getCurrentPanel())
1567 {
1568 getCurrentPanel()->setFocus(TRUE);
1569 }
1570 handled = TRUE;
1571 break;
1572 case KEY_LEFT:
1573 selectPrevTab();
1574 handled = TRUE;
1575 break;
1576 case KEY_RIGHT:
1577 selectNextTab();
1578 handled = TRUE;
1579 break;
1580 default:
1581 break;
1582 }
1583 }
1584 return handled;
1585}
1586
1587// virtual
1588LLXMLNodePtr LLTabContainer::getXML(bool save_children) const
1589{
1590 LLXMLNodePtr node = LLTabContainerCommon::getXML();
1591
1592 node->createChild("tab_position", TRUE)->setStringValue((mTabPosition == TOP ? "top" : "bottom"));
1593
1594 return node;
1595}
1596
1597BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, LLString &tooltip)
1598{
1599 BOOL has_scroll_arrows = (mMaxScrollPos > 0);
1600
1601 if( mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME )
1602 {
1603
1604 if (has_scroll_arrows)
1605 {
1606 if (mJumpLeftArrowBtn->getRect().pointInRect(x, y))
1607 {
1608 S32 local_x = x - mJumpLeftArrowBtn->getRect().mLeft;
1609 S32 local_y = y - mJumpLeftArrowBtn->getRect().mBottom;
1610 mJumpLeftArrowBtn->handleHover(local_x, local_y, mask);
1611 }
1612 if (mJumpRightArrowBtn->getRect().pointInRect(x, y))
1613 {
1614 S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft;
1615 S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom;
1616 mJumpRightArrowBtn->handleHover(local_x, local_y, mask);
1617 }
1618 if (mLeftArrowBtn->getRect().pointInRect(x, y))
1619 {
1620 S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
1621 S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
1622 mLeftArrowBtn->handleHover(local_x, local_y, mask);
1623 }
1624 else if (mRightArrowBtn->getRect().pointInRect(x, y))
1625 {
1626 S32 local_x = x - mRightArrowBtn->getRect().mLeft;
1627 S32 local_y = y - mRightArrowBtn->getRect().mBottom;
1628 mRightArrowBtn->handleHover(local_x, local_y, mask);
1629 }
1630 }
1631
1632 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1633 {
1634 LLTabTuple* tuple = *iter;
1635 tuple->mButton->setVisible( TRUE );
1636 S32 local_x = x - tuple->mButton->getRect().mLeft;
1637 S32 local_y = y - tuple->mButton->getRect().mBottom;
1638 if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
1639 { 1808 {
1640 tuple->mButton->onCommit(); 1809 tuple->mButton->onCommit();
1641 mDragAndDropDelayTimer.stop();
1642 } 1810 }
1643 } 1811 }
1644 } 1812 }
1645
1646 return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip);
1647} 1813}
1648 1814
1649void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
1650{
1651 LLTabTuple* tuple = getTabByPanel(child);
1652 if( tuple )
1653 {
1654 tuple->mButton->setImageOverlay(image_name, LLFontGL::RIGHT, color);
1655
1656 const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
1657 // remove current width from total tab strip width
1658 mTotalTabWidth -= tuple->mButton->getRect().getWidth();
1659
1660 S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ?
1661 tuple->mButton->getImageOverlay()->getImage()->getWidth(0) :
1662 0;
1663
1664 tuple->mPadding = image_overlay_width;
1665
1666 tuple->mButton->setRightHPad(6);
1667 tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth),
1668 tuple->mButton->getRect().getHeight());
1669 // add back in button width to total tab strip width
1670 mTotalTabWidth += tuple->mButton->getRect().getWidth();
1671
1672 // tabs have changed size, might need to scroll to see current tab
1673 updateMaxScrollPos();
1674 }
1675}