/** * @file lllayoutstack.cpp * @brief LLLayout class - dynamic stacking of UI elements * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ // Opaque view with a background and a border. Can contain LLUICtrls. #include "linden_common.h" #include "lllayoutstack.h" #include "lllocalcliprect.h" #include "llpanel.h" #include "llresizebar.h" #include "llcriticaldamp.h" static LLDefaultChildRegistry::Register register_layout_stack("layout_stack", &LLLayoutStack::fromXML); // // LLLayoutStack // 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) { LLResizeBar::Side side = (orientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM; LLRect resize_bar_rect = panelp->getRect(); 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(p); // panels initialized as hidden should not start out partially visible if (!mPanel->getVisible()) { mVisibleAmt = 0.f; } } ~LayoutPanel() { // probably not necessary, but... delete mResizeBar; mResizeBar = NULL; } F32 getCollapseFactor() { if (mOrientation == HORIZONTAL) { F32 collapse_amt = clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinWidth / (F32)llmax(1, mPanel->getRect().getWidth())); return mVisibleAmt * collapse_amt; } else { 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; }; LLLayoutStack::Params::Params() : orientation("orientation", std::string("vertical")), animate("animate", true), clip("clip", true), border_size("border_size", LLCachedControl(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0)) { name="stack"; } LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) : LLView(p), mMinWidth(0), mMinHeight(0), mPanelSpacing(p.border_size), mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL), mAnimate(p.animate), mAnimatedThisFrame(false), mClip(p.clip) {} LLLayoutStack::~LLLayoutStack() { e_panel_list_t panels = mPanels; // copy list of panel pointers mPanels.clear(); // clear so that removeChild() calls don't cause trouble std::for_each(panels.begin(), panels.end(), DeletePointer()); } void LLLayoutStack::draw() { updateLayout(); e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { // clip to layout rectangle, not bounding rectangle LLRect clip_rect = (*panel_it)->mPanel->getRect(); // scale clipping rectangle by visible amount if (mOrientation == HORIZONTAL) { clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->getCollapseFactor()); } else { clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->getCollapseFactor()); } 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()); } mAnimatedThisFrame = false; } void LLLayoutStack::removeChild(LLView* view) { LayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast(view)); if (embedded_panelp) { mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); delete embedded_panelp; } // need to update resizebars calcMinExtents(); LLView::removeChild(view); } BOOL LLLayoutStack::postBuild() { updateLayout(); return TRUE; } static void get_attribute_s32_and_write(LLXMLNodePtr node, const char* name, S32 *value, S32 default_value, LLXMLNodePtr output_child) { BOOL has_attr = node->getAttributeS32(name, *value); if (has_attr && *value != default_value && output_child) { // create an attribute child node LLXMLNodePtr child_attr = output_child->createChild(name, TRUE); child_attr->setIntValue(*value); } } static void get_attribute_bool_and_write(LLXMLNodePtr node, const char* name, BOOL *value, BOOL default_value, LLXMLNodePtr output_child) { BOOL has_attr = node->getAttributeBOOL(name, *value); if (has_attr && *value != default_value && output_child) { LLXMLNodePtr child_attr = output_child->createChild(name, TRUE); child_attr->setBoolValue(*value); } } //static LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node) { LLLayoutStack::Params p(LLUICtrlFactory::getDefaultParams()); LLXUIParser::instance().readXUI(node, p, LLUICtrlFactory::getInstance()->getCurFileName()); // 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()); output_node->setName(node->getName()->mString); LLXUIParser::instance().writeXUI( output_node, output_params, &default_params); } p.from_xui = true; setupParams(p, parent); LLLayoutStack* layout_stackp = LLUICtrlFactory::create(p); if (parent && layout_stackp) { S32 tab_group = p.tab_group.isProvided() ? p.tab_group() : parent->getLastTabGroup(); parent->addChild(layout_stackp, tab_group); } for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling()) { 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: