diff options
Diffstat (limited to 'indra/llui/lllayoutstack.cpp')
-rwxr-xr-x[-rw-r--r--] | indra/llui/lllayoutstack.cpp | 1260 |
1 files changed, 754 insertions, 506 deletions
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 5eade72b61..69246a2f57 100644..100755 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -2,31 +2,25 @@ * @file lllayoutstack.cpp * @brief LLLayout class - dynamic stacking of UI elements * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,107 +32,211 @@ #include "lllocalcliprect.h" #include "llpanel.h" -#include "llresizebar.h" #include "llcriticaldamp.h" +#include "lliconctrl.h" +#include "boost/foreach.hpp" -static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack", &LLLayoutStack::fromXML); +static const F32 MIN_FRACTIONAL_SIZE = 0.00001f; +static const F32 MAX_FRACTIONAL_SIZE = 1.f; +static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack"); +static LLLayoutStack::LayoutStackRegistry::Register<LLLayoutPanel> register_layout_panel("layout_panel"); // -// LLLayoutStack +// LLLayoutPanel // -struct LLLayoutStack::LayoutPanel -{ - LayoutPanel(LLPanel* panelp, ELayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize) : mPanel(panelp), - mMinWidth(min_width), - mMinHeight(min_height), - mAutoResize(auto_resize), - mUserResize(user_resize), - mOrientation(orientation), - mCollapsed(FALSE), - mCollapseAmt(0.f), - mVisibleAmt(1.f), // default to fully visible - mResizeBar(NULL) +LLLayoutPanel::Params::Params() +: expanded_min_dim("expanded_min_dim", 0), + min_dim("min_dim", -1), + user_resize("user_resize", false), + auto_resize("auto_resize", true) +{ + addSynonym(min_dim, "min_width"); + addSynonym(min_dim, "min_height"); +} + +LLLayoutPanel::LLLayoutPanel(const Params& p) +: LLPanel(p), + mExpandedMinDim(p.expanded_min_dim.isProvided() ? p.expanded_min_dim : p.min_dim), + mMinDim(p.min_dim), + mAutoResize(p.auto_resize), + mUserResize(p.user_resize), + mCollapsed(FALSE), + mCollapseAmt(0.f), + mVisibleAmt(1.f), // default to fully visible + mResizeBar(NULL), + mFractionalSize(0.f), + mTargetDim(0), + mIgnoreReshape(false), + mOrientation(LLLayoutStack::HORIZONTAL) +{ + // panels initialized as hidden should not start out partially visible + if (!getVisible()) { - LLResizeBar::Side side = (orientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM; - LLRect resize_bar_rect = panelp->getRect(); + mVisibleAmt = 0.f; + } +} - S32 min_dim; - if (orientation == HORIZONTAL) - { - min_dim = mMinHeight; - } - else - { - min_dim = mMinWidth; - } - LLResizeBar::Params p; - p.name("resize"); - p.resizing_view(mPanel); - p.min_size(min_dim); - p.side(side); - p.snapping_enabled(false); - mResizeBar = LLUICtrlFactory::create<LLResizeBar>(p); - // panels initialized as hidden should not start out partially visible - if (!mPanel->getVisible()) - { - mVisibleAmt = 0.f; - } +void LLLayoutPanel::initFromParams(const Params& p) +{ + LLPanel::initFromParams(p); + setFollowsNone(); +} + + +LLLayoutPanel::~LLLayoutPanel() +{ + // probably not necessary, but... + delete mResizeBar; + mResizeBar = NULL; +} + +F32 LLLayoutPanel::getAutoResizeFactor() const +{ + return mVisibleAmt * (1.f - mCollapseAmt); +} + +F32 LLLayoutPanel::getVisibleAmount() const +{ + return mVisibleAmt; +} + +S32 LLLayoutPanel::getLayoutDim() const +{ + return ll_round((F32)((mOrientation == LLLayoutStack::HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight())); +} + +S32 LLLayoutPanel::getTargetDim() const +{ + return mTargetDim; +} + +void LLLayoutPanel::setTargetDim(S32 value) +{ + LLRect new_rect(getRect()); + if (mOrientation == LLLayoutStack::HORIZONTAL) + { + new_rect.mRight = new_rect.mLeft + value; + } + else + { + new_rect.mTop = new_rect.mBottom + value; } + setShape(new_rect, true); +} - ~LayoutPanel() +S32 LLLayoutPanel::getVisibleDim() const +{ + F32 min_dim = getRelevantMinDim(); + return ll_round(mVisibleAmt + * (min_dim + + (((F32)mTargetDim - min_dim) * (1.f - mCollapseAmt)))); +} + +void LLLayoutPanel::setOrientation( LLView::EOrientation orientation ) +{ + mOrientation = orientation; + S32 layout_dim = ll_round((F32)((mOrientation == LLLayoutStack::HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight())); + + if (mAutoResize == FALSE + && mUserResize == TRUE + && mMinDim == -1 ) { - // probably not necessary, but... - delete mResizeBar; - mResizeBar = NULL; + setMinDim(layout_dim); } - - F32 getCollapseFactor() + mTargetDim = llmax(layout_dim, getMinDim()); +} + +void LLLayoutPanel::setVisible( BOOL visible ) +{ + if (visible != getVisible()) { - if (mOrientation == HORIZONTAL) + LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent()); + if (stackp) { - F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinWidth / (F32)llmax(1, mPanel->getRect().getWidth())); - return mVisibleAmt * collapse_amt; + stackp->mNeedsLayout = true; } - else + } + LLPanel::setVisible(visible); +} + +void LLLayoutPanel::reshape( S32 width, S32 height, BOOL called_from_parent /*= TRUE*/ ) +{ + if (width == getRect().getWidth() && height == getRect().getHeight()) return; + + if (!mIgnoreReshape && mAutoResize == false) { - F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinHeight / (F32)llmax(1, mPanel->getRect().getHeight()))); - return mVisibleAmt * collapse_amt; - } - } - - LLPanel* mPanel; - S32 mMinWidth; - S32 mMinHeight; - BOOL mAutoResize; - BOOL mUserResize; - BOOL mCollapsed; - LLResizeBar* mResizeBar; - ELayoutOrientation mOrientation; - F32 mVisibleAmt; - F32 mCollapseAmt; -}; + mTargetDim = (mOrientation == LLLayoutStack::HORIZONTAL) ? width : height; + LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent()); + if (stackp) + { + stackp->mNeedsLayout = true; + } + } + LLPanel::reshape(width, height, called_from_parent); +} + +void LLLayoutPanel::handleReshape(const LLRect& new_rect, bool by_user) +{ + LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent()); + if (stackp) + { + if (by_user) + { // tell layout stack to account for new shape + + // make sure that panels have already been auto resized + stackp->updateLayout(); + // now apply requested size to panel + stackp->updatePanelRect(this, new_rect); + } + stackp->mNeedsLayout = true; + } + LLPanel::handleReshape(new_rect, by_user); +} + +// +// LLLayoutStack +// LLLayoutStack::Params::Params() -: orientation("orientation", std::string("vertical")), +: orientation("orientation"), animate("animate", true), clip("clip", true), - border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0)) + open_time_constant("open_time_constant", 0.02f), + close_time_constant("close_time_constant", 0.03f), + resize_bar_overlap("resize_bar_overlap", 1), + border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0)), + show_drag_handle("show_drag_handle", false), + drag_handle_first_indent("drag_handle_first_indent", 0), + drag_handle_second_indent("drag_handle_second_indent", 0), + drag_handle_thickness("drag_handle_thickness", 5), + drag_handle_shift("drag_handle_shift", 2) { - name="stack"; + addSynonym(border_size, "drag_handle_gap"); } LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) : LLView(p), - mMinWidth(0), - mMinHeight(0), mPanelSpacing(p.border_size), - mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL), + mOrientation(p.orientation), mAnimate(p.animate), - mClip(p.clip) -{} + mAnimatedThisFrame(false), + mNeedsLayout(true), + mClip(p.clip), + mOpenTimeConstant(p.open_time_constant), + mCloseTimeConstant(p.close_time_constant), + mResizeBarOverlap(p.resize_bar_overlap), + mShowDragHandle(p.show_drag_handle), + mDragHandleFirstIndent(p.drag_handle_first_indent), + mDragHandleSecondIndent(p.drag_handle_second_indent), + mDragHandleThickness(p.drag_handle_thickness), + mDragHandleShift(p.drag_handle_shift) +{ +} LLLayoutStack::~LLLayoutStack() { @@ -151,43 +249,45 @@ void LLLayoutStack::draw() { updateLayout(); - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + // always clip to stack itself + LLLocalClipRect clip(getLocalRect()); + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) { // clip to layout rectangle, not bounding rectangle - LLRect clip_rect = (*panel_it)->mPanel->getRect(); + LLRect clip_rect = panelp->getRect(); // scale clipping rectangle by visible amount if (mOrientation == HORIZONTAL) { - clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->getCollapseFactor()); + clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim(); } else { - clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->getCollapseFactor()); + clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim(); } - LLPanel* panelp = (*panel_it)->mPanel; - - LLLocalClipRect clip(clip_rect, mClip); - // only force drawing invisible children if visible amount is non-zero - drawChild(panelp, 0, 0, !clip_rect.isEmpty()); + {LLLocalClipRect clip(clip_rect, mClip); + // only force drawing invisible children if visible amount is non-zero + drawChild(panelp, 0, 0, !clip_rect.isEmpty()); + } + if (panelp->getResizeBar()->getVisible()) + { + drawChild(panelp->getResizeBar()); + } } } void LLLayoutStack::removeChild(LLView* view) { - LayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view)); + LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view)); if (embedded_panelp) { mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); delete embedded_panelp; + updateFractionalSizes(); + mNeedsLayout = true; } - // need to update resizebars - - calcMinExtents(); - LLView::removeChild(view); } @@ -197,182 +297,324 @@ BOOL LLLayoutStack::postBuild() return TRUE; } -static void get_attribute_s32_and_write(LLXMLNodePtr node, - const char* name, - S32 *value, - S32 default_value, - LLXMLNodePtr output_child) +bool LLLayoutStack::addChild(LLView* child, S32 tab_group) { - BOOL has_attr = node->getAttributeS32(name, *value); - if (has_attr && *value != default_value && output_child) + LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child); + if (panelp) { - // create an attribute child node - LLXMLNodePtr child_attr = output_child->createChild(name, TRUE); - child_attr->setIntValue(*value); + panelp->setOrientation(mOrientation); + mPanels.push_back(panelp); + createResizeBar(panelp); + mNeedsLayout = true; } + BOOL result = LLView::addChild(child, tab_group); + + updateFractionalSizes(); + return result; } -static void get_attribute_bool_and_write(LLXMLNodePtr node, - const char* name, - BOOL *value, - BOOL default_value, - LLXMLNodePtr output_child) +void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate) { - BOOL has_attr = node->getAttributeBOOL(name, *value); - if (has_attr && *value != default_value && output_child) + addChild(panel); + + // panel starts off invisible (collapsed) + if (animate == ANIMATE) { - LLXMLNodePtr child_attr = output_child->createChild(name, TRUE); - child_attr->setBoolValue(*value); + panel->mVisibleAmt = 0.f; + panel->setVisible(TRUE); } } -//static -LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node) + +void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed) { - LLLayoutStack::Params p(LLUICtrlFactory::getDefaultParams<LLLayoutStack>()); - LLXUIParser::instance().readXUI(node, p); + LLLayoutPanel* panel_container = findEmbeddedPanel(panel); + if (!panel_container) return; - // Export must happen before setupParams() mungles rectangles and before - // this item gets added to parent (otherwise screws up last_child_rect - // logic). JC - if (output_node) - { - Params output_params(p); - setupParamsForExport(output_params, parent); - LLLayoutStack::Params default_params(LLUICtrlFactory::getDefaultParams<LLLayoutStack>()); - output_node->setName(node->getName()->mString); - LLXUIParser::instance().writeXUI( - output_node, output_params, &default_params); - } + panel_container->mCollapsed = collapsed; + mNeedsLayout = true; +} - setupParams(p, parent); - LLLayoutStack* layout_stackp = LLUICtrlFactory::create<LLLayoutStack>(p); +static LLTrace::BlockTimerStatHandle FTM_UPDATE_LAYOUT("Update LayoutStacks"); - if (parent && layout_stackp) +class LLImagePanel : public LLPanel +{ +public: + struct Params : public LLInitParam::Block<Params, LLPanel::Params> { - S32 tab_group = p.tab_group.isProvided() ? p.tab_group() : parent->getLastTabGroup(); + Optional<bool> horizontal; + Params() : horizontal("horizontal", false) {} + }; + LLImagePanel(const Params& p) : LLPanel(p), mHorizontal(p.horizontal) {} + virtual ~LLImagePanel() {} - parent->addChild(layout_stackp, tab_group); + void draw() + { + const LLRect& parent_rect = getParent()->getRect(); + const LLRect& rect = getRect(); + LLRect clip_rect( -rect.mLeft, parent_rect.getHeight() - rect.mBottom - 2 + , parent_rect.getWidth() - rect.mLeft - (mHorizontal ? 2 : 0), -rect.mBottom); + LLLocalClipRect clip(clip_rect); + LLPanel::draw(); } - for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling()) +private: + bool mHorizontal; +}; + +void LLLayoutStack::updateLayout() +{ + LL_RECORD_BLOCK_TIME(FTM_UPDATE_LAYOUT); + + if (!mNeedsLayout) return; + + bool continue_animating = animatePanels(); + F32 total_visible_fraction = 0.f; + S32 space_to_distribute = (mOrientation == HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight(); + + // first, assign minimum dimensions + LLLayoutPanel* panelp = NULL; + BOOST_FOREACH(panelp, mPanels) { - const S32 DEFAULT_MIN_WIDTH = 0; - const S32 DEFAULT_MIN_HEIGHT = 0; - const BOOL DEFAULT_AUTO_RESIZE = TRUE; - - S32 min_width = DEFAULT_MIN_WIDTH; - S32 min_height = DEFAULT_MIN_HEIGHT; - BOOL auto_resize = DEFAULT_AUTO_RESIZE; - - LLXMLNodePtr output_child; - if (output_node) - { - output_child = output_node->createChild("", FALSE); - } - - // Layout stack allows child nodes to acquire additional attributes, - // such as "min_width" in: <button label="Foo" min_width="100"/> - // If these attributes exist and have non-default values, write them - // to the output node. - get_attribute_s32_and_write(child_node, "min_width", &min_width, - DEFAULT_MIN_WIDTH, output_child); - get_attribute_s32_and_write(child_node, "min_height", &min_height, - DEFAULT_MIN_HEIGHT, output_child); - get_attribute_bool_and_write(child_node, "auto_resize", &auto_resize, - DEFAULT_AUTO_RESIZE, output_child); - - if (child_node->hasName("layout_panel")) - { - BOOL user_resize = TRUE; - get_attribute_bool_and_write(child_node, "user_resize", &user_resize, - TRUE, output_child); - LLPanel* panelp = (LLPanel*)LLPanel::fromXML(child_node, layout_stackp, output_child); - if (panelp) + if (panelp->mAutoResize) + { + panelp->mTargetDim = panelp->getRelevantMinDim(); + } + space_to_distribute -= panelp->getVisibleDim() + ll_round((F32)mPanelSpacing * panelp->getVisibleAmount()); + total_visible_fraction += panelp->mFractionalSize * panelp->getAutoResizeFactor(); + } + + llassert(total_visible_fraction < 1.05f); + + // don't need spacing after last panel + space_to_distribute += panelp ? ll_round((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0; + + S32 remaining_space = space_to_distribute; + F32 fraction_distributed = 0.f; + if (space_to_distribute > 0 && total_visible_fraction > 0.f) + { // give space proportionally to visible auto resize panels + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) { - panelp->setFollowsNone(); - layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); + F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction); + S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute); + fraction_distributed += fraction_to_distribute; + panelp->mTargetDim += delta; + remaining_space -= delta; } } + } + + // distribute any left over pixels to non-collapsed, visible panels + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (remaining_space == 0) break; + + if (panelp->mAutoResize + && !panelp->mCollapsed + && panelp->getVisible()) + { + S32 space_for_panel = remaining_space > 0 ? 1 : -1; + panelp->mTargetDim += space_for_panel; + remaining_space -= space_for_panel; + } + } + + F32 cur_pos = (mOrientation == HORIZONTAL) ? 0.f : (F32)getRect().getHeight(); + + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + F32 panel_dim = llmax(panelp->getExpandedMinDim(), panelp->mTargetDim); + + LLRect panel_rect; + if (mOrientation == HORIZONTAL) + { + panel_rect.setLeftTopAndSize(ll_round(cur_pos), + getRect().getHeight(), + ll_round(panel_dim), + getRect().getHeight()); + } else { - BOOL user_resize = FALSE; - get_attribute_bool_and_write(child_node, "user_resize", &user_resize, - FALSE, output_child); + panel_rect.setLeftTopAndSize(0, + ll_round(cur_pos), + getRect().getWidth(), + ll_round(panel_dim)); + } + + LLRect resize_bar_rect(panel_rect); + F32 panel_spacing = (F32)mPanelSpacing * panelp->getVisibleAmount(); + F32 panel_visible_dim = panelp->getVisibleDim(); + S32 panel_spacing_round = (S32)(ll_round(panel_spacing)); - LLPanel::Params p; - p.mouse_opaque(false); - LLPanel* panelp = LLUICtrlFactory::create<LLPanel>(p); - LLView* new_child = LLUICtrlFactory::getInstance()->createFromXML(child_node, panelp, LLStringUtil::null, LLPanel::child_registry_t::instance(), output_child); - if (new_child) + if (mOrientation == HORIZONTAL) + { + cur_pos += panel_visible_dim + panel_spacing; + + if (mShowDragHandle && panel_spacing_round > mDragHandleThickness) { - // put child in new embedded panel - layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); - // resize panel to contain widget and move widget to be contained in panel - panelp->setRect(new_child->getRect()); - new_child->setOrigin(0, 0); + resize_bar_rect.mLeft = panel_rect.mRight + mDragHandleShift; + resize_bar_rect.mRight = resize_bar_rect.mLeft + mDragHandleThickness; } else { - panelp->die(); + resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap; + resize_bar_rect.mRight = panel_rect.mRight + panel_spacing_round + mResizeBarOverlap; + } + + if (mShowDragHandle) + { + resize_bar_rect.mBottom += mDragHandleSecondIndent; + resize_bar_rect.mTop -= mDragHandleFirstIndent; } + } - - if (output_child && !output_child->mChildren && output_child->mAttributes.empty() && output_child->getValue().empty()) + else //VERTICAL { - output_node->deleteChild(output_child); + cur_pos -= panel_visible_dim + panel_spacing; + + if (mShowDragHandle && panel_spacing_round > mDragHandleThickness) + { + resize_bar_rect.mTop = panel_rect.mBottom - mDragHandleShift; + resize_bar_rect.mBottom = resize_bar_rect.mTop - mDragHandleThickness; + } + else + { + resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap; + resize_bar_rect.mBottom = panel_rect.mBottom - panel_spacing_round - mResizeBarOverlap; + } + + if (mShowDragHandle) + { + resize_bar_rect.mLeft += mDragHandleFirstIndent; + resize_bar_rect.mRight -= mDragHandleSecondIndent; + } } - } - if (!layout_stackp->postBuild()) - { - delete layout_stackp; - return NULL; + panelp->setIgnoreReshape(true); + panelp->setShape(panel_rect); + panelp->setIgnoreReshape(false); + panelp->mResizeBar->setShape(resize_bar_rect); } - return layout_stackp; -} + updateResizeBarLimits(); -S32 LLLayoutStack::getDefaultHeight(S32 cur_height) + // clear animation flag at end, since panel resizes will set it + // and leave it set if there is any animation in progress + mNeedsLayout = continue_animating; +} // end LLLayoutStack::updateLayout + +LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const { - // if we are spanning our children (crude upward propagation of size) - // then don't enforce our size on our children - if (mOrientation == HORIZONTAL) + if (!panelp) return NULL; + + e_panel_list_t::const_iterator panel_it; + BOOST_FOREACH(LLLayoutPanel* p, mPanels) { - cur_height = llmax(mMinHeight, getRect().getHeight()); + if (p == panelp) + { + return p; + } } - - return cur_height; + return NULL; } -S32 LLLayoutStack::getDefaultWidth(S32 cur_width) +LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const { - // if we are spanning our children (crude upward propagation of size) - // then don't enforce our size on our children - if (mOrientation == VERTICAL) + LLLayoutPanel* result = NULL; + + BOOST_FOREACH(LLLayoutPanel* p, mPanels) { - cur_width = llmax(mMinWidth, getRect().getWidth()); + if (p->getName() == name) + { + result = p; + break; + } } - return cur_width; + return result; } -void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate, S32 index) +void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) { - // panel starts off invisible (collapsed) - if (animate == ANIMATE) + BOOST_FOREACH(LLLayoutPanel* lp, mPanels) { - panel->setVisible(FALSE); - } - LayoutPanel* embedded_panel = new LayoutPanel(panel, mOrientation, min_width, min_height, auto_resize, user_resize); - - mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel); - - if (panel->getParent() != this) - { - addChild(panel); - } - addChild(embedded_panel->mResizeBar); + if (lp->mResizeBar == NULL) + { + LLResizeBar::Params resize_params; + resize_params.name("resize"); + resize_params.resizing_view(lp); + resize_params.min_size(lp->getRelevantMinDim()); + resize_params.side((mOrientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM); + resize_params.snapping_enabled(false); + LLResizeBar* resize_bar = LLUICtrlFactory::create<LLResizeBar>(resize_params); + lp->mResizeBar = resize_bar; + + if (mShowDragHandle) + { + LLPanel::Params resize_bar_bg_panel_p; + resize_bar_bg_panel_p.name = "resize_handle_bg_panel"; + resize_bar_bg_panel_p.rect = lp->mResizeBar->getLocalRect(); + resize_bar_bg_panel_p.follows.flags = FOLLOWS_ALL; + resize_bar_bg_panel_p.tab_stop = false; + resize_bar_bg_panel_p.background_visible = true; + resize_bar_bg_panel_p.bg_alpha_color = LLUIColorTable::instance().getColor("ResizebarBody"); + resize_bar_bg_panel_p.has_border = true; + resize_bar_bg_panel_p.border.border_thickness = 1; + resize_bar_bg_panel_p.border.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight"); + resize_bar_bg_panel_p.border.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark"); + + LLPanel* resize_bar_bg_panel = LLUICtrlFactory::create<LLPanel>(resize_bar_bg_panel_p); + + LLIconCtrl::Params icon_p; + icon_p.name = "resize_handle_image"; + icon_p.rect = lp->mResizeBar->getLocalRect(); + icon_p.follows.flags = FOLLOWS_ALL; + icon_p.image = LLUI::getUIImage(mOrientation == HORIZONTAL ? "Vertical Drag Handle" : "Horizontal Drag Handle"); + resize_bar_bg_panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p)); + + lp->mResizeBar->addChild(resize_bar_bg_panel); + } + /*if (mShowDragHandle) + { + LLViewBorder::Params border_params; + border_params.border_thickness = 1; + border_params.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight"); + border_params.shadow_dark_color = LLUIColorTable::instance().getColor("ResizebarBorderDark"); + + addBorder(border_params); + setBorderVisible(TRUE); + + LLImagePanel::Params image_panel; + mDragHandleImage = LLUI::getUIImage(LLResizeBar::RIGHT == mSide ? "Vertical Drag Handle" : "Horizontal Drag Handle"); + image_panel.bg_alpha_image = mDragHandleImage; + image_panel.background_visible = true; + image_panel.horizontal = (LLResizeBar::BOTTOM == mSide); + mImagePanel = LLUICtrlFactory::create<LLImagePanel>(image_panel); + setImagePanel(mImagePanel); + }*/ + + //if (mShowDragHandle) + //{ + // setBackgroundVisible(TRUE); + // setTransparentColor(LLUIColorTable::instance().getColor("ResizebarBody")); + //} + + /*if (mShowDragHandle) + { + S32 image_width = mDragHandleImage->getTextureWidth(); + S32 image_height = mDragHandleImage->getTextureHeight(); + const LLRect& panel_rect = getRect(); + S32 image_left = (panel_rect.getWidth() - image_width) / 2 - 1; + S32 image_bottom = (panel_rect.getHeight() - image_height) / 2; + mImagePanel->setRect(LLRect(image_left, image_bottom + image_height, image_left + image_width, image_bottom)); + }*/ + LLView::addChild(resize_bar, 0); + } + } // bring all resize bars to the front so that they are clickable even over the panels // with a bit of overlap for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) @@ -380,367 +622,373 @@ void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL LLResizeBar* resize_barp = (*panel_it)->mResizeBar; sendChildToFront(resize_barp); } +} - // start expanding panel animation - if (animate == ANIMATE) +// update layout stack animations, etc. once per frame +// NOTE: we use this to size world view based on animating UI, *before* we draw the UI +// we might still need to call updateLayout during UI draw phase, in case UI elements +// are resizing themselves dynamically +//static +void LLLayoutStack::updateClass() +{ + for (instance_iter it = beginInstances(); it != endInstances(); ++it) { - panel->setVisible(TRUE); + it->updateLayout(); + it->mAnimatedThisFrame = false; } } -void LLLayoutStack::removePanel(LLPanel* panel) +void LLLayoutStack::updateFractionalSizes() { - removeChild(panel); -} + F32 total_resizable_dim = 0.f; -void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed) -{ - LayoutPanel* panel_container = findEmbeddedPanel(panel); - if (!panel_container) return; + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); + } + } - panel_container->mCollapsed = collapsed; + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + F32 panel_resizable_dim = llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); + panelp->mFractionalSize = panel_resizable_dim > 0.f + ? llclamp(panel_resizable_dim / total_resizable_dim, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE) + : MIN_FRACTIONAL_SIZE; + llassert(!llisnan(panelp->mFractionalSize)); + } + } + + normalizeFractionalSizes(); } -void LLLayoutStack::updateLayout(BOOL force_resize) -{ - static LLUICachedControl<S32> resize_bar_overlap ("UIResizeBarOverlap", 0); - calcMinExtents(); - // calculate current extents - S32 total_width = 0; - S32 total_height = 0; +void LLLayoutStack::normalizeFractionalSizes() +{ + S32 num_auto_resize_panels = 0; + F32 total_fractional_size = 0.f; + + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + total_fractional_size += panelp->mFractionalSize; + num_auto_resize_panels++; + } + } - const F32 ANIM_OPEN_TIME = 0.02f; - const F32 ANIM_CLOSE_TIME = 0.03f; + if (total_fractional_size == 0.f) + { // equal distribution + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels; + } + } + } + else + { // renormalize + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + panelp->mFractionalSize /= total_fractional_size; + } + } + } +} - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) +bool LLLayoutStack::animatePanels() +{ + bool continue_animating = false; + + // + // animate visibility + // + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) { - LLPanel* panelp = (*panel_it)->mPanel; - if (panelp->getVisible()) + if (panelp->getVisible()) { - if (mAnimate) + if (mAnimate && panelp->mVisibleAmt < 1.f) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME)); - if ((*panel_it)->mVisibleAmt > 0.99f) + if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = 1.f; + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLSmoothInterpolation::getInterpolant(mOpenTimeConstant)); + if (panelp->mVisibleAmt > 0.99f) + { + panelp->mVisibleAmt = 1.f; + } } + + mAnimatedThisFrame = true; + continue_animating = true; } else { - (*panel_it)->mVisibleAmt = 1.f; + if (panelp->mVisibleAmt != 1.f) + { + panelp->mVisibleAmt = 1.f; + mAnimatedThisFrame = true; + } } } else // not visible { - if (mAnimate) + if (mAnimate && panelp->mVisibleAmt > 0.f) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - if ((*panel_it)->mVisibleAmt < 0.001f) + if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = 0.f; + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant)); + if (panelp->mVisibleAmt < 0.001f) + { + panelp->mVisibleAmt = 0.f; + } } + + continue_animating = true; + mAnimatedThisFrame = true; } else { - (*panel_it)->mVisibleAmt = 0.f; + if (panelp->mVisibleAmt != 0.f) + { + panelp->mVisibleAmt = 0.f; + mAnimatedThisFrame = true; + } } } - if ((*panel_it)->mCollapsed) - { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - } - else + F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f; + if (panelp->mCollapseAmt != collapse_state) { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - } - - if (mOrientation == HORIZONTAL) - { - // enforce minimize size constraint by default - if (panelp->getRect().getWidth() < (*panel_it)->mMinWidth) - { - panelp->reshape((*panel_it)->mMinWidth, panelp->getRect().getHeight()); - } - total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor()); - // want n-1 panel gaps for n panels - if (panel_it != mPanels.begin()) - { - total_width += mPanelSpacing; - } - } - else //VERTICAL - { - // enforce minimize size constraint by default - if (panelp->getRect().getHeight() < (*panel_it)->mMinHeight) + if (mAnimate) { - panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinHeight); + if (!mAnimatedThisFrame) + { + panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, collapse_state, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant)); + } + + if (llabs(panelp->mCollapseAmt - collapse_state) < 0.001f) + { + panelp->mCollapseAmt = collapse_state; + } + + mAnimatedThisFrame = true; + continue_animating = true; } - total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor()); - if (panel_it != mPanels.begin()) + else { - total_height += mPanelSpacing; + panelp->mCollapseAmt = collapse_state; + mAnimatedThisFrame = true; } } } - S32 num_resizable_panels = 0; - S32 shrink_headroom_available = 0; - S32 shrink_headroom_total = 0; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + if (mAnimatedThisFrame) mNeedsLayout = true; + return continue_animating; +} + +void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& new_rect ) +{ + S32 new_dim = (mOrientation == HORIZONTAL) + ? new_rect.getWidth() + : new_rect.getHeight(); + S32 delta_panel_dim = new_dim - resized_panel->getVisibleDim(); + if (delta_panel_dim == 0) return; + + F32 total_visible_fraction = 0.f; + F32 delta_auto_resize_headroom = 0.f; + F32 old_auto_resize_headroom = 0.f; + + LLLayoutPanel* other_resize_panel = NULL; + LLLayoutPanel* following_panel = NULL; + + BOOST_REVERSE_FOREACH(LLLayoutPanel* panelp, mPanels) { - // panels that are not fully visible do not count towards shrink headroom - if ((*panel_it)->getCollapseFactor() < 1.f) + if (panelp->mAutoResize) { - continue; + old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim()); + if (panelp->getVisible() && !panelp->mCollapsed) + { + total_visible_fraction += panelp->mFractionalSize; + } } - // if currently resizing a panel or the panel is flagged as not automatically resizing - // only track total available headroom, but don't use it for automatic resize logic - if ((*panel_it)->mResizeBar->hasMouseCapture() - || (!(*panel_it)->mAutoResize - && !force_resize)) + if (panelp == resized_panel) { - if (mOrientation == HORIZONTAL) - { - shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - } - else //VERTICAL - { - shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - } + other_resize_panel = following_panel; } - else + + if (panelp->getVisible() && !panelp->mCollapsed) { - num_resizable_panels++; - if (mOrientation == HORIZONTAL) - { - shrink_headroom_available += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - } - else //VERTICAL - { - shrink_headroom_available += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - } + following_panel = panelp; } } - // calculate how many pixels need to be distributed among layout panels - // positive means panels need to grow, negative means shrink - S32 pixels_to_distribute; - if (mOrientation == HORIZONTAL) + if (resized_panel->mAutoResize) { - pixels_to_distribute = getRect().getWidth() - total_width; + if (!other_resize_panel || !other_resize_panel->mAutoResize) + { + delta_auto_resize_headroom += delta_panel_dim; + } } - else //VERTICAL + else { - pixels_to_distribute = getRect().getHeight() - total_height; + if (!other_resize_panel || other_resize_panel->mAutoResize) + { + delta_auto_resize_headroom -= delta_panel_dim; + } } - // now we distribute the pixels... - S32 cur_x = 0; - S32 cur_y = getRect().getHeight(); + F32 fraction_given_up = 0.f; + F32 fraction_remaining = 1.f; + F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + enum { - LLPanel* panelp = (*panel_it)->mPanel; + BEFORE_RESIZED_PANEL, + RESIZED_PANEL, + NEXT_PANEL, + AFTER_RESIZED_PANEL + } which_panel = BEFORE_RESIZED_PANEL; - S32 cur_width = panelp->getRect().getWidth(); - S32 cur_height = panelp->getRect().getHeight(); - S32 new_width = llmax((*panel_it)->mMinWidth, cur_width); - S32 new_height = llmax((*panel_it)->mMinHeight, cur_height); + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (!panelp->getVisible() || panelp->mCollapsed) + { + if (panelp->mAutoResize) + { + fraction_remaining -= panelp->mFractionalSize; + } + continue; + } - S32 delta_size = 0; + if (panelp == resized_panel) + { + which_panel = RESIZED_PANEL; + } - // if panel can automatically resize (not animating, and resize flag set)... - if ((*panel_it)->getCollapseFactor() == 1.f - && (force_resize || (*panel_it)->mAutoResize) - && !(*panel_it)->mResizeBar->hasMouseCapture()) + switch(which_panel) { - if (mOrientation == HORIZONTAL) - { - // if we're shrinking - if (pixels_to_distribute < 0) - { - // shrink proportionally to amount over minimum - // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinWidth) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_width - (*panel_it)->mMinWidth); - } - else - { - // grow all elements equally - delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); - num_resizable_panels--; - } - pixels_to_distribute -= delta_size; - new_width = llmax((*panel_it)->mMinWidth, cur_width + delta_size); + case BEFORE_RESIZED_PANEL: + if (panelp->mAutoResize) + { // freeze current size as fraction of overall auto_resize space + F32 fractional_adjustment_factor = new_auto_resize_headroom == 0.f + ? 1.f + : old_auto_resize_headroom / new_auto_resize_headroom; + F32 new_fractional_size = llclamp(panelp->mFractionalSize * fractional_adjustment_factor, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + fraction_remaining -= panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; + llassert(!llisnan(panelp->mFractionalSize)); } else { - new_width = getDefaultWidth(new_width); + // leave non auto-resize panels alone } - - if (mOrientation == VERTICAL) + break; + case RESIZED_PANEL: + if (panelp->mAutoResize) + { // freeze new size as fraction + F32 new_fractional_size = (new_auto_resize_headroom == 0.f) + ? MAX_FRACTIONAL_SIZE + : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / new_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + fraction_remaining -= panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; + llassert(!llisnan(panelp->mFractionalSize)); + } + else + { // freeze new size as original size + panelp->mTargetDim = new_dim; + } + which_panel = NEXT_PANEL; + break; + case NEXT_PANEL: + if (panelp->mAutoResize) { - if (pixels_to_distribute < 0) + fraction_remaining -= panelp->mFractionalSize; + if (resized_panel->mAutoResize) { - // shrink proportionally to amount over minimum - // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinHeight) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_height - (*panel_it)->mMinHeight); + panelp->mFractionalSize = llclamp(panelp->mFractionalSize + fraction_given_up, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); + fraction_given_up = 0.f; } else { - delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); - num_resizable_panels--; + if (new_auto_resize_headroom < 1.f) + { + new_auto_resize_headroom = 1.f; + } + + F32 new_fractional_size = llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom) + / new_auto_resize_headroom, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; } - pixels_to_distribute -= delta_size; - new_height = llmax((*panel_it)->mMinHeight, cur_height + delta_size); } else { - new_height = getDefaultHeight(new_height); - } - } - else - { - if (mOrientation == HORIZONTAL) - { - new_height = getDefaultHeight(new_height); + panelp->mTargetDim -= delta_panel_dim; } - else // VERTICAL + which_panel = AFTER_RESIZED_PANEL; + break; + case AFTER_RESIZED_PANEL: + if (panelp->mAutoResize && fraction_given_up != 0.f) { - new_width = getDefaultWidth(new_width); + panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); } - } - - // adjust running headroom count based on new sizes - shrink_headroom_total += delta_size; - - panelp->reshape(new_width, new_height); - panelp->setOrigin(cur_x, cur_y - new_height); - - LLRect panel_rect = panelp->getRect(); - LLRect resize_bar_rect = panel_rect; - if (mOrientation == HORIZONTAL) - { - resize_bar_rect.mLeft = panel_rect.mRight - resize_bar_overlap; - resize_bar_rect.mRight = panel_rect.mRight + mPanelSpacing + resize_bar_overlap; - } - else - { - resize_bar_rect.mTop = panel_rect.mBottom + resize_bar_overlap; - resize_bar_rect.mBottom = panel_rect.mBottom - mPanelSpacing - resize_bar_overlap; - } - (*panel_it)->mResizeBar->setRect(resize_bar_rect); - - if (mOrientation == HORIZONTAL) - { - cur_x += llround(new_width * (*panel_it)->getCollapseFactor()) + mPanelSpacing; - } - else //VERTICAL - { - cur_y -= llround(new_height * (*panel_it)->getCollapseFactor()) + mPanelSpacing; - } - } - - // update resize bars with new limits - LLResizeBar* last_resize_bar = NULL; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLPanel* panelp = (*panel_it)->mPanel; - - if (mOrientation == HORIZONTAL) - { - (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinWidth, - (*panel_it)->mMinWidth + shrink_headroom_total); - } - else //VERTICAL - { - (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinHeight, - (*panel_it)->mMinHeight + shrink_headroom_total); - } - - // toggle resize bars based on panel visibility, resizability, etc - BOOL resize_bar_enabled = panelp->getVisible() && (*panel_it)->mUserResize; - (*panel_it)->mResizeBar->setVisible(resize_bar_enabled); - - if (resize_bar_enabled) - { - last_resize_bar = (*panel_it)->mResizeBar; + default: + break; } } + updateLayout(); + //normalizeFractionalSizes(); +} - // hide last resize bar as there is nothing past it - // resize bars need to be in between two resizable panels - if (last_resize_bar) - { - last_resize_bar->setVisible(FALSE); - } - - // not enough room to fit existing contents - if (force_resize == FALSE - // layout did not complete by reaching target position - && ((mOrientation == VERTICAL && cur_y != -mPanelSpacing) - || (mOrientation == HORIZONTAL && cur_x != getRect().getWidth() + mPanelSpacing))) - { - // do another layout pass with all stacked elements contributing - // even those that don't usually resize - llassert_always(force_resize == FALSE); - updateLayout(TRUE); - } -} // end LLLayoutStack::updateLayout - - -LLLayoutStack::LayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const +void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent) { - if (!panelp) return NULL; + mNeedsLayout = true; + LLView::reshape(width, height, called_from_parent); +} - e_panel_list_t::const_iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) +void LLLayoutStack::updateResizeBarLimits() +{ + LLLayoutPanel* previous_visible_panelp = NULL; + BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels) { - if ((*panel_it)->mPanel == panelp) + if (!visible_panelp->getVisible() || visible_panelp->mCollapsed) { - return *panel_it; + visible_panelp->mResizeBar->setVisible(FALSE); + continue; } - } - return NULL; -} -// Compute sum of min_width or min_height of children -void LLLayoutStack::calcMinExtents() -{ - mMinWidth = 0; - mMinHeight = 0; - - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - if (mOrientation == HORIZONTAL) + // toggle resize bars based on panel visibility, resizability, etc + if (previous_visible_panelp + && (visible_panelp->mUserResize || previous_visible_panelp->mUserResize) // one of the pair is user resizable + && (visible_panelp->mAutoResize || visible_panelp->mUserResize) // current panel is resizable + && (previous_visible_panelp->mAutoResize || previous_visible_panelp->mUserResize)) // previous panel is resizable { - mMinHeight = llmax( mMinHeight, - (*panel_it)->mMinHeight); - mMinWidth += (*panel_it)->mMinWidth; - if (panel_it != mPanels.begin()) - { - mMinWidth += mPanelSpacing; - } + visible_panelp->mResizeBar->setVisible(TRUE); + S32 previous_panel_headroom = previous_visible_panelp->getVisibleDim() - previous_visible_panelp->getRelevantMinDim(); + visible_panelp->mResizeBar->setResizeLimits(visible_panelp->getRelevantMinDim(), + visible_panelp->getVisibleDim() + previous_panel_headroom); } - else //VERTICAL + else { - mMinWidth = llmax( mMinWidth, - (*panel_it)->mMinWidth); - mMinHeight += (*panel_it)->mMinHeight; - if (panel_it != mPanels.begin()) - { - mMinHeight += mPanelSpacing; - } + visible_panelp->mResizeBar->setVisible(FALSE); } + + previous_visible_panelp = visible_panelp; } } + |