PageRenderTime 72ms CodeModel.GetById 17ms app.highlight 49ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llui/lllayoutstack.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 800 lines | 649 code | 95 blank | 56 comment | 111 complexity | 44cd588d9f2320f16c68a48d62cc69e3 MD5 | raw file
  1/** 
  2 * @file lllayoutstack.cpp
  3 * @brief LLLayout class - dynamic stacking of UI elements
  4 *
  5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27// Opaque view with a background and a border.  Can contain LLUICtrls.
 28
 29#include "linden_common.h"
 30
 31#include "lllayoutstack.h"
 32
 33#include "lllocalcliprect.h"
 34#include "llpanel.h"
 35#include "llresizebar.h"
 36#include "llcriticaldamp.h"
 37#include "boost/foreach.hpp"
 38
 39static const F32 MIN_FRACTIONAL_SIZE = 0.0001f;
 40static const F32 MAX_FRACTIONAL_SIZE = 1.f;
 41
 42static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack");
 43static LLLayoutStack::LayoutStackRegistry::Register<LLLayoutPanel> register_layout_panel("layout_panel");
 44
 45void LLLayoutStack::OrientationNames::declareValues()
 46{
 47	declare("horizontal", HORIZONTAL);
 48	declare("vertical", VERTICAL);
 49}
 50
 51//
 52// LLLayoutPanel
 53//
 54LLLayoutPanel::Params::Params()	
 55:	expanded_min_dim("expanded_min_dim", 0),
 56	min_dim("min_dim", -1),
 57	user_resize("user_resize", false),
 58	auto_resize("auto_resize", true)
 59{
 60	addSynonym(min_dim, "min_width");
 61	addSynonym(min_dim, "min_height");
 62}
 63
 64LLLayoutPanel::LLLayoutPanel(const Params& p)	
 65:	LLPanel(p),
 66	mExpandedMinDim(p.expanded_min_dim.isProvided() ? p.expanded_min_dim : p.min_dim),
 67 	mMinDim(p.min_dim), 
 68 	mAutoResize(p.auto_resize),
 69 	mUserResize(p.user_resize),
 70	mCollapsed(FALSE),
 71	mCollapseAmt(0.f),
 72	mVisibleAmt(1.f), // default to fully visible
 73	mResizeBar(NULL),
 74	mFractionalSize(MIN_FRACTIONAL_SIZE),
 75	mTargetDim(0),
 76	mIgnoreReshape(false),
 77	mOrientation(LLLayoutStack::HORIZONTAL)
 78{
 79	// panels initialized as hidden should not start out partially visible
 80	if (!getVisible())
 81	{
 82		mVisibleAmt = 0.f;
 83	}
 84}
 85
 86void LLLayoutPanel::initFromParams(const Params& p)
 87{
 88	LLPanel::initFromParams(p);
 89	setFollowsNone();
 90}
 91
 92
 93LLLayoutPanel::~LLLayoutPanel()
 94{
 95	// probably not necessary, but...
 96	delete mResizeBar;
 97	mResizeBar = NULL;
 98}
 99
100F32 LLLayoutPanel::getAutoResizeFactor() const
101{
102	return mVisibleAmt * (1.f - mCollapseAmt);
103}
104 
105F32 LLLayoutPanel::getVisibleAmount() const
106{
107	return mVisibleAmt;
108}
109
110S32 LLLayoutPanel::getLayoutDim() const
111{
112	return llround((F32)((mOrientation == LLLayoutStack::HORIZONTAL)
113					? getRect().getWidth()
114					: getRect().getHeight()));
115}
116 
117S32 LLLayoutPanel::getVisibleDim() const
118{
119	F32 min_dim = getRelevantMinDim();
120	return llround(mVisibleAmt
121					* (min_dim
122						+ (((F32)mTargetDim - min_dim) * (1.f - mCollapseAmt))));
123}
124 
125void LLLayoutPanel::setOrientation( LLLayoutStack::ELayoutOrientation orientation )
126{
127	mOrientation = orientation;
128	S32 layout_dim = llround((F32)((mOrientation == LLLayoutStack::HORIZONTAL)
129		? getRect().getWidth()
130		: getRect().getHeight()));
131
132	mTargetDim = llmax(layout_dim, getMinDim());
133}
134 
135void LLLayoutPanel::setVisible( BOOL visible )
136{
137	if (visible != getVisible())
138	{
139		LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent());
140		if (stackp)
141		{
142			stackp->mNeedsLayout = true;
143		}
144	}
145	LLPanel::setVisible(visible);
146}
147
148void LLLayoutPanel::reshape( S32 width, S32 height, BOOL called_from_parent /*= TRUE*/ )
149{
150	if (width == getRect().getWidth() && height == getRect().getHeight()) return;
151
152	if (!mIgnoreReshape && mAutoResize == false)
153	{
154		mTargetDim = (mOrientation == LLLayoutStack::HORIZONTAL) ? width : height;
155		LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent());
156		if (stackp)
157		{
158			stackp->mNeedsLayout = true;
159		}
160	}
161	LLPanel::reshape(width, height, called_from_parent);
162}
163
164void LLLayoutPanel::handleReshape(const LLRect& new_rect, bool by_user)
165{
166	LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent());
167	if (stackp)
168	{
169		stackp->mNeedsLayout = true;
170		if (by_user)
171		{
172			// tell layout stack to account for new shape
173			stackp->updatePanelRect(this, new_rect);
174		}
175	}
176	LLPanel::handleReshape(new_rect, by_user);
177}
178
179//
180// LLLayoutStack
181//
182
183LLLayoutStack::Params::Params()
184:	orientation("orientation"),
185	animate("animate", true),
186	clip("clip", true),
187	open_time_constant("open_time_constant", 0.02f),
188	close_time_constant("close_time_constant", 0.03f),
189	resize_bar_overlap("resize_bar_overlap", 1),
190	border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0))
191{}
192
193LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) 
194:	LLView(p),
195	mPanelSpacing(p.border_size),
196	mOrientation(p.orientation),
197	mAnimate(p.animate),
198	mAnimatedThisFrame(false),
199	mNeedsLayout(true),
200	mClip(p.clip),
201	mOpenTimeConstant(p.open_time_constant),
202	mCloseTimeConstant(p.close_time_constant),
203	mResizeBarOverlap(p.resize_bar_overlap)
204{}
205
206LLLayoutStack::~LLLayoutStack()
207{
208	e_panel_list_t panels = mPanels; // copy list of panel pointers
209	mPanels.clear(); // clear so that removeChild() calls don't cause trouble
210	std::for_each(panels.begin(), panels.end(), DeletePointer());
211}
212
213void LLLayoutStack::draw()
214{
215	updateLayout();
216
217	// always clip to stack itself
218	LLLocalClipRect clip(getLocalRect());
219	BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
220	{
221		// clip to layout rectangle, not bounding rectangle
222		LLRect clip_rect = panelp->getRect();
223		// scale clipping rectangle by visible amount
224		if (mOrientation == HORIZONTAL)
225		{
226			clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim();
227		}
228		else
229		{
230			clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim();
231		}
232
233		{LLLocalClipRect clip(clip_rect, mClip);
234			// only force drawing invisible children if visible amount is non-zero
235			drawChild(panelp, 0, 0, !clip_rect.isEmpty());
236		}
237	}
238	mAnimatedThisFrame = false;
239}
240
241void LLLayoutStack::removeChild(LLView* view)
242{
243	LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view));
244
245	if (embedded_panelp)
246	{
247		mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp));
248		delete embedded_panelp;
249		updateFractionalSizes();
250		mNeedsLayout = true;
251	}
252
253	LLView::removeChild(view);
254}
255
256BOOL LLLayoutStack::postBuild()
257{
258	updateLayout();
259	return TRUE;
260}
261
262bool LLLayoutStack::addChild(LLView* child, S32 tab_group)
263{
264	LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child);
265	if (panelp)
266	{
267		panelp->setOrientation(mOrientation);
268		mPanels.push_back(panelp);
269		createResizeBar(panelp);
270		mNeedsLayout = true;
271	}
272	BOOL result = LLView::addChild(child, tab_group);
273
274	updateFractionalSizes();
275	return result;
276}
277
278void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate)
279{
280	addChild(panel);
281
282	// panel starts off invisible (collapsed)
283	if (animate == ANIMATE)
284	{
285		panel->mVisibleAmt = 0.f;
286		panel->setVisible(TRUE);
287	}
288}
289
290void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed)
291{
292	LLLayoutPanel* panel_container = findEmbeddedPanel(panel);
293	if (!panel_container) return;
294
295	panel_container->mCollapsed = collapsed;
296	mNeedsLayout = true;
297}
298
299static LLFastTimer::DeclareTimer FTM_UPDATE_LAYOUT("Update LayoutStacks");
300
301void LLLayoutStack::updateLayout()
302{	
303	LLFastTimer ft(FTM_UPDATE_LAYOUT);
304
305	if (!mNeedsLayout) return;
306
307	bool animation_in_progress = animatePanels();
308	F32 total_visible_fraction = 0.f;
309	F32 total_open_fraction = 0.f;
310	S32 space_to_distribute = (mOrientation == HORIZONTAL)
311							? getRect().getWidth()
312							: getRect().getHeight();
313
314	// first, assign minimum dimensions
315	LLLayoutPanel* panelp = NULL;
316	BOOST_FOREACH(panelp, mPanels)
317	{
318		if (panelp->mAutoResize)
319		{
320			panelp->mTargetDim = panelp->getRelevantMinDim();
321			if (!panelp->mCollapsed && panelp->getVisible())
322			{
323				total_open_fraction += panelp->mFractionalSize;
324			}
325		}
326		space_to_distribute -= panelp->getVisibleDim() + llround((F32)mPanelSpacing * panelp->getVisibleAmount());
327		total_visible_fraction += panelp->mFractionalSize;
328	}
329
330	llassert(total_visible_fraction < 1.01f);
331
332	// don't need spacing after last panel
333	space_to_distribute += panelp ? llround((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0;
334
335	F32 fraction_distributed = 0.f;
336	if (space_to_distribute > 0 && total_visible_fraction > 0.f)
337	{	// give space proportionally to visible auto resize panels
338		BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
339		{
340			if (panelp->mAutoResize)
341			{
342				F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction);
343				S32 delta = llround((F32)space_to_distribute * fraction_to_distribute);
344				fraction_distributed += fraction_to_distribute;
345				panelp->mTargetDim += delta;
346			}
347		}
348	}
349
350	if (fraction_distributed < total_visible_fraction)
351	{	// distribute any left over pixels to non-collapsed, visible panels
352		F32 fraction_left = total_visible_fraction - fraction_distributed;
353		S32 space_left = llround((F32)space_to_distribute * (fraction_left / total_visible_fraction));
354
355		BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
356		{
357			if (panelp->mAutoResize 
358				&& !panelp->mCollapsed 
359				&& panelp->getVisible())
360			{
361				S32 space_for_panel = llmax(0, llround((F32)space_left * (panelp->mFractionalSize / total_open_fraction)));
362				panelp->mTargetDim += space_for_panel;
363				space_left -= space_for_panel;
364				total_open_fraction -= panelp->mFractionalSize;
365			}
366		}
367	}
368
369	F32 cur_pos = (mOrientation == HORIZONTAL) ? 0.f : (F32)getRect().getHeight();
370
371	BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
372	{
373		F32 panel_dim = llmax(panelp->getExpandedMinDim(), panelp->mTargetDim);
374		F32 panel_visible_dim = panelp->getVisibleDim();
375
376		LLRect panel_rect;
377		if (mOrientation == HORIZONTAL)
378		{
379			panel_rect.setLeftTopAndSize(llround(cur_pos),
380										getRect().getHeight(),
381										llround(panel_dim),
382										getRect().getHeight());
383		}
384		else
385		{
386			panel_rect.setLeftTopAndSize(0,
387										llround(cur_pos),
388										getRect().getWidth(),
389										llround(panel_dim));
390		}
391		panelp->setIgnoreReshape(true);
392		panelp->setShape(panel_rect);
393		panelp->setIgnoreReshape(false);
394
395		LLRect resize_bar_rect(panel_rect);
396
397		F32 panel_spacing = (F32)mPanelSpacing * panelp->getVisibleAmount();
398		if (mOrientation == HORIZONTAL)
399		{
400			resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap;
401			resize_bar_rect.mRight = panel_rect.mRight + (S32)(llround(panel_spacing)) + mResizeBarOverlap;
402
403			cur_pos += panel_visible_dim + panel_spacing;
404		}
405		else //VERTICAL
406		{
407			resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap;
408			resize_bar_rect.mBottom = panel_rect.mBottom - (S32)(llround(panel_spacing)) - mResizeBarOverlap;
409
410			cur_pos -= panel_visible_dim + panel_spacing;
411		}
412		panelp->mResizeBar->setShape(resize_bar_rect);
413	}
414
415	updateResizeBarLimits();
416
417	// clear animation flag at end, since panel resizes will set it
418	// and leave it set if there is any animation in progress
419	mNeedsLayout = animation_in_progress;
420} // end LLLayoutStack::updateLayout
421
422LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
423{
424	if (!panelp) return NULL;
425
426	e_panel_list_t::const_iterator panel_it;
427	BOOST_FOREACH(LLLayoutPanel* p, mPanels)
428	{
429		if (p == panelp)
430		{
431			return p;
432		}
433	}
434	return NULL;
435}
436
437LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
438{
439	LLLayoutPanel* result = NULL;
440
441	BOOST_FOREACH(LLLayoutPanel* p, mPanels)
442	{
443		if (p->getName() == name)
444		{
445			result = p;
446			break;
447		}
448	}
449
450	return result;
451}
452
453void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp)
454{
455	BOOST_FOREACH(LLLayoutPanel* lp, mPanels)
456	{
457		if (lp->mResizeBar == NULL)
458		{
459			LLResizeBar::Side side = (mOrientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM;
460			LLRect resize_bar_rect = getRect();
461
462			LLResizeBar::Params resize_params;
463			resize_params.name("resize");
464			resize_params.resizing_view(lp);
465			resize_params.min_size(lp->getRelevantMinDim());
466			resize_params.side(side);
467			resize_params.snapping_enabled(false);
468			LLResizeBar* resize_bar = LLUICtrlFactory::create<LLResizeBar>(resize_params);
469			lp->mResizeBar = resize_bar;
470			LLView::addChild(resize_bar, 0);
471		}
472	}
473	// bring all resize bars to the front so that they are clickable even over the panels
474	// with a bit of overlap
475	for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it)
476	{
477		LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
478		sendChildToFront(resize_barp);
479	}
480}
481
482// update layout stack animations, etc. once per frame
483// NOTE: we use this to size world view based on animating UI, *before* we draw the UI
484// we might still need to call updateLayout during UI draw phase, in case UI elements
485// are resizing themselves dynamically
486//static 
487void LLLayoutStack::updateClass()
488{
489	for (instance_iter it = beginInstances(); it != endInstances(); ++it)
490	{
491		it->updateLayout();
492	}
493}
494
495void LLLayoutStack::updateFractionalSizes()
496{
497	F32 total_resizable_dim = 0;
498	S32 num_auto_resize_panels = 0;
499
500	BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
501	{
502		if (panelp->mAutoResize)
503		{
504			total_resizable_dim += llmax(0, panelp->getLayoutDim() - panelp->getRelevantMinDim());
505			num_auto_resize_panels++;
506		}
507	}
508
509	F32 total_fractional_size = 0.f;
510	
511	BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
512	{
513		if (panelp->mAutoResize)
514		{
515			F32 panel_resizable_dim = llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim()));
516			panelp->mFractionalSize = panel_resizable_dim > 0.f 
517										? llclamp(panel_resizable_dim / total_resizable_dim, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE)
518										: MIN_FRACTIONAL_SIZE;
519			total_fractional_size += panelp->mFractionalSize;
520			llassert(!llisnan(panelp->mFractionalSize));
521		}
522	}
523
524	if (total_fractional_size == 0.f)
525	{ // equal distribution
526		BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
527		{
528			if (panelp->mAutoResize)
529			{
530				panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels;
531			}
532		}
533	}
534	else
535	{ // renormalize
536		BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
537		{
538			if (panelp->mAutoResize)
539			{
540				panelp->mFractionalSize /= total_fractional_size;
541			}
542		}
543	}
544}
545
546bool LLLayoutStack::animatePanels()
547{
548	bool animation_in_progress = false;
549	
550	//
551	// animate visibility
552	//
553	BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
554	{
555		if (panelp->getVisible())
556		{
557			if (mAnimate && panelp->mVisibleAmt < 1.f)
558			{
559				if (!mAnimatedThisFrame)
560				{
561					panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant));
562					if (panelp->mVisibleAmt > 0.99f)
563					{
564						panelp->mVisibleAmt = 1.f;
565					}
566				}
567				
568				animation_in_progress = true;
569			}
570			else
571			{
572				if (panelp->mVisibleAmt != 1.f)
573				{
574					panelp->mVisibleAmt = 1.f;
575					animation_in_progress = true;
576				}
577			}
578		}
579		else // not visible
580		{
581			if (mAnimate && panelp->mVisibleAmt > 0.f)
582			{
583				if (!mAnimatedThisFrame)
584				{
585					panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
586					if (panelp->mVisibleAmt < 0.001f)
587					{
588						panelp->mVisibleAmt = 0.f;
589					}
590				}
591
592				animation_in_progress = true;
593			}
594			else
595			{
596				if (panelp->mVisibleAmt != 0.f)
597				{
598					panelp->mVisibleAmt = 0.f;
599					animation_in_progress = true;
600				}
601			}
602		}
603
604		F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f;
605		if (panelp->mCollapseAmt != collapse_state)
606		{
607			if (!mAnimatedThisFrame)
608			{
609				panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, collapse_state, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
610			}
611			animation_in_progress = true;
612			
613			if (llabs(panelp->mCollapseAmt - collapse_state) < 0.001f)
614			{
615				panelp->mCollapseAmt = collapse_state;
616			}
617		}
618	}
619
620	mAnimatedThisFrame = true;
621
622	return animation_in_progress;
623}
624
625void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& new_rect )
626{
627	S32 new_dim = (mOrientation == HORIZONTAL)
628					? new_rect.getWidth()
629					: new_rect.getHeight();
630	S32 delta_dim = new_dim - resized_panel->getVisibleDim();
631	if (delta_dim == 0) return;
632
633	F32 total_visible_fraction = 0.f;
634	F32 delta_auto_resize_headroom = 0.f;
635	F32 total_auto_resize_headroom = 0.f;
636
637	LLLayoutPanel* other_resize_panel = NULL;
638	LLLayoutPanel* following_panel = NULL;
639
640	BOOST_REVERSE_FOREACH(LLLayoutPanel* panelp, mPanels)
641	{
642		if (panelp->mAutoResize)
643		{
644			total_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim());
645			total_visible_fraction += panelp->mFractionalSize * panelp->getAutoResizeFactor();
646		}
647
648		if (panelp == resized_panel)
649		{
650			other_resize_panel = following_panel;
651		}
652
653		if (panelp->getVisible() && !panelp->mCollapsed)
654		{
655			following_panel = panelp;
656		}
657	}
658
659	if (resized_panel->mAutoResize == FALSE)
660	{
661		delta_auto_resize_headroom += -delta_dim;
662	}
663	if (other_resize_panel && other_resize_panel->mAutoResize == FALSE)
664	{
665		delta_auto_resize_headroom += delta_dim;
666	}
667
668	F32 fraction_given_up = 0.f;
669	F32 fraction_remaining = 1.f;
670	F32 updated_auto_resize_headroom = total_auto_resize_headroom + delta_auto_resize_headroom;
671
672	enum
673	{
674		BEFORE_RESIZED_PANEL,
675		RESIZED_PANEL,
676		NEXT_PANEL,
677		AFTER_RESIZED_PANEL
678	} which_panel = BEFORE_RESIZED_PANEL;
679
680	BOOST_FOREACH(LLLayoutPanel* panelp, mPanels)
681	{
682		if (!panelp->getVisible() || panelp->mCollapsed) continue;
683
684		if (panelp == resized_panel)
685		{
686			which_panel = RESIZED_PANEL;
687		}
688
689		switch(which_panel)
690		{
691		case BEFORE_RESIZED_PANEL:
692			if (panelp->mAutoResize)
693			{	// freeze current size as fraction of overall auto_resize space
694				F32 fractional_adjustment_factor = total_auto_resize_headroom / updated_auto_resize_headroom;
695				F32 new_fractional_size = llclamp(panelp->mFractionalSize * fractional_adjustment_factor,
696													MIN_FRACTIONAL_SIZE,
697													MAX_FRACTIONAL_SIZE);
698				F32 fraction_delta = (new_fractional_size - panelp->mFractionalSize);
699				fraction_given_up -= fraction_delta;
700				fraction_remaining -= panelp->mFractionalSize;
701				panelp->mFractionalSize += fraction_delta;
702				llassert(!llisnan(panelp->mFractionalSize));
703			}
704			else
705			{
706				// leave non auto-resize panels alone
707			}
708			break;
709		case RESIZED_PANEL:
710			if (panelp->mAutoResize)
711			{	// freeze new size as fraction
712				F32 new_fractional_size = (updated_auto_resize_headroom == 0.f)
713					? MAX_FRACTIONAL_SIZE
714					: llclamp((F32)(new_dim - panelp->getRelevantMinDim()) / updated_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE);
715				fraction_given_up -= new_fractional_size - panelp->mFractionalSize;
716				fraction_remaining -= panelp->mFractionalSize;
717				panelp->mFractionalSize = new_fractional_size;
718				llassert(!llisnan(panelp->mFractionalSize));
719			}
720			else
721			{	// freeze new size as original size
722				panelp->mTargetDim = new_dim;
723				fraction_remaining -= fraction_given_up;
724			}
725			which_panel = NEXT_PANEL;
726			break;
727		case NEXT_PANEL:
728			if (panelp->mAutoResize)
729			{
730				fraction_remaining -= panelp->mFractionalSize;
731				if (fraction_given_up != 0.f)
732				{
733					panelp->mFractionalSize = llclamp(panelp->mFractionalSize + fraction_given_up, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE);
734					fraction_given_up = 0.f;
735				}
736				else
737				{
738					F32 new_fractional_size = llclamp((F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom) 
739														/ updated_auto_resize_headroom,
740													MIN_FRACTIONAL_SIZE,
741													MAX_FRACTIONAL_SIZE);
742					fraction_given_up -= new_fractional_size - panelp->mFractionalSize;
743					panelp->mFractionalSize = new_fractional_size;
744				}
745			}
746			else
747			{
748				panelp->mTargetDim -= delta_dim;
749			}
750			which_panel = AFTER_RESIZED_PANEL;
751			break;
752		case AFTER_RESIZED_PANEL:
753			if (panelp->mAutoResize)
754			{
755				panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up,
756												MIN_FRACTIONAL_SIZE,
757												MAX_FRACTIONAL_SIZE);
758			}
759		default:
760			break;
761		}
762	}
763}
764
765void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent)
766{
767	mNeedsLayout = true;
768	LLView::reshape(width, height, called_from_parent);
769}
770
771void LLLayoutStack::updateResizeBarLimits()
772{
773	LLLayoutPanel* previous_visible_panelp = NULL;
774	BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels)
775	{
776		if (!visible_panelp->getVisible() || visible_panelp->mCollapsed)
777		{
778			visible_panelp->mResizeBar->setVisible(FALSE);
779			continue;
780		}
781
782		// toggle resize bars based on panel visibility, resizability, etc
783		if (previous_visible_panelp
784			&& (visible_panelp->mUserResize || previous_visible_panelp->mUserResize)				// one of the pair is user resizable
785			&& (visible_panelp->mAutoResize || visible_panelp->mUserResize)							// current panel is resizable
786			&& (previous_visible_panelp->mAutoResize || previous_visible_panelp->mUserResize))		// previous panel is resizable
787		{
788			visible_panelp->mResizeBar->setVisible(TRUE);
789			S32 previous_panel_headroom = previous_visible_panelp->getVisibleDim() - previous_visible_panelp->getRelevantMinDim();
790			visible_panelp->mResizeBar->setResizeLimits(visible_panelp->getRelevantMinDim(), 
791														visible_panelp->getVisibleDim() + previous_panel_headroom);
792		}
793		else
794		{
795			visible_panelp->mResizeBar->setVisible(FALSE);
796		}
797
798		previous_visible_panelp = visible_panelp;
799	}
800}