diff options
Diffstat (limited to 'indra/llui/lllayoutstack.cpp')
-rw-r--r-- | indra/llui/lllayoutstack.cpp | 2044 |
1 files changed, 1022 insertions, 1022 deletions
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index cf8cfa6ea6..1b6a785f27 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -1,1022 +1,1022 @@ -/** - * @file lllayoutstack.cpp - * @brief LLLayout class - dynamic stacking of UI elements - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * 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. - * - * 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. - * - * 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 - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/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 "llcriticaldamp.h" -#include "lliconctrl.h" -#include "boost/foreach.hpp" - -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"); - -// -// LLLayoutPanel -// -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()) - { - mVisibleAmt = 0.f; - } -} - -void LLLayoutPanel::initFromParams(const Params& p) -{ - LLPanel::initFromParams(p); - setFollowsNone(); -} - - -LLLayoutPanel::~LLLayoutPanel() -{ - // probably not necessary, but... - delete mResizeBar; - mResizeBar = NULL; - - gFocusMgr.removeKeyboardFocusWithoutCallback(this); -} - -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); -} - -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 && mUserResize && mMinDim == -1) - { - setMinDim(layout_dim); - } - mTargetDim = llmax(layout_dim, getMinDim()); -} - -void LLLayoutPanel::setVisible( bool visible ) -{ - if (visible != getVisible()) - { - LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent()); - if (stackp) - { - stackp->mNeedsLayout = true; - } - } - LLPanel::setVisible(visible); -} - -void LLLayoutPanel::reshape( S32 width, S32 height, bool called_from_parent /*= true*/ ) -{ - if (width == getRect().getWidth() && height == getRect().getHeight() && !LLView::sForceReshape) return; - - if (!mIgnoreReshape && !mAutoResize) - { - 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"), - animate("animate", true), - clip("clip", true), - 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::getInstance()->mSettingGroups["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), - drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody")) -{ - addSynonym(border_size, "drag_handle_gap"); -} - -LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) -: LLView(p), - mPanelSpacing(p.border_size), - mOrientation(p.orientation), - mAnimate(p.animate), - 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), - mDragHandleColor(p.drag_handle_color()) -{ -} - -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(); - - // always clip to stack itself - LLLocalClipRect clip(getLocalRect()); - for (LLLayoutPanel* panelp : mPanels) - { - if ((!panelp->getVisible() || panelp->mCollapsed) - && (panelp->mVisibleAmt < 0.001f || !mAnimate)) - { - // essentially invisible - continue; - } - // clip to layout rectangle, not bounding rectangle - LLRect clip_rect = panelp->getRect(); - // scale clipping rectangle by visible amount - if (mOrientation == HORIZONTAL) - { - clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim(); - } - else - { - clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim(); - } - - {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::deleteAllChildren() -{ - mPanels.clear(); - LLView::deleteAllChildren(); - - // Not really needed since nothing is left to - // display, but for the sake of consistency - updateFractionalSizes(); - mNeedsLayout = true; -} - -void LLLayoutStack::removeChild(LLView* view) -{ - LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view)); - - if (embedded_panelp) - { - mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); - LLView::removeChild(view); - updateFractionalSizes(); - mNeedsLayout = true; - } - else - { - LLView::removeChild(view); - } -} - -bool LLLayoutStack::postBuild() -{ - updateLayout(); - return true; -} - -bool LLLayoutStack::addChild(LLView* child, S32 tab_group) -{ - LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child); - if (panelp) - { - panelp->setOrientation(mOrientation); - mPanels.push_back(panelp); - createResizeBar(panelp); - mNeedsLayout = true; - } - bool result = LLView::addChild(child, tab_group); - - updateFractionalSizes(); - return result; -} - -void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate) -{ - addChild(panel); - - // panel starts off invisible (collapsed) - if (animate == ANIMATE) - { - panel->mVisibleAmt = 0.f; - panel->setVisible(true); - } -} - -void LLLayoutStack::collapsePanel(LLPanel* panel, bool collapsed) -{ - LLLayoutPanel* panel_container = findEmbeddedPanel(panel); - if (!panel_container) return; - - panel_container->mCollapsed = collapsed; - mNeedsLayout = true; -} - -class LLImagePanel : public LLPanel -{ -public: - struct Params : public LLInitParam::Block<Params, LLPanel::Params> - { - Optional<bool> horizontal; - Params() : horizontal("horizontal", false) {} - }; - LLImagePanel(const Params& p) : LLPanel(p), mHorizontal(p.horizontal) {} - virtual ~LLImagePanel() {} - - 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(); - } - -private: - bool mHorizontal; -}; - -void LLLayoutStack::updateLayout() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; - - 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 - for (LLLayoutPanel* panelp : mPanels) - { - 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 - if (!mPanels.empty()) - { - space_to_distribute += ll_round(F32(mPanelSpacing) * mPanels.back()->getVisibleAmount()); - } - - S32 remaining_space = space_to_distribute; - if (space_to_distribute > 0 && total_visible_fraction > 0.f) - { // give space proportionally to visible auto resize panels - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction); - S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute); - panelp->mTargetDim += delta; - remaining_space -= delta; - } - } - } - - // distribute any left over pixels to non-collapsed, visible panels - for (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(); - - for (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 - { - 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)); - - if (mOrientation == HORIZONTAL) - { - cur_pos += panel_visible_dim + panel_spacing; - - if (mShowDragHandle && panel_spacing_round > mDragHandleThickness) - { - resize_bar_rect.mLeft = panel_rect.mRight + mDragHandleShift; - resize_bar_rect.mRight = resize_bar_rect.mLeft + mDragHandleThickness; - } - else - { - 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; - } - - } - else //VERTICAL - { - 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; - } - } - - panelp->setIgnoreReshape(true); - panelp->setShape(panel_rect); - panelp->setIgnoreReshape(false); - panelp->mResizeBar->setShape(resize_bar_rect); - } - - updateResizeBarLimits(); - - // 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 - -void LLLayoutStack::setPanelSpacing(S32 val) -{ - if (mPanelSpacing != val) - { - mPanelSpacing = val; - mNeedsLayout = true; - } -} - -LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const -{ - if (!panelp) return NULL; - - for (LLLayoutPanel* p : mPanels) - { - if (p == panelp) - { - return p; - } - } - return NULL; -} - -LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const -{ - LLLayoutPanel* result = NULL; - - for (LLLayoutPanel* p : mPanels) - { - if (p->getName() == name) - { - result = p; - break; - } - } - - return result; -} - -void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) -{ - for (LLLayoutPanel* lp : mPanels) - { - 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 = mDragHandleColor; - 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) - { - LLResizeBar* resize_barp = (*panel_it)->mResizeBar; - sendChildToFront(resize_barp); - } -} - -// 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 (auto& layout : instance_snapshot()) - { - layout.updateLayout(); - layout.mAnimatedThisFrame = false; - } -} - -void LLLayoutStack::updateFractionalSizes() -{ - F32 total_resizable_dim = 0.f; - - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); - } - } - - for (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::normalizeFractionalSizes() -{ - S32 num_auto_resize_panels = 0; - F32 total_fractional_size = 0.f; - - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - total_fractional_size += panelp->mFractionalSize; - num_auto_resize_panels++; - } - } - - if (total_fractional_size == 0.f) - { // equal distribution - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels; - } - } - } - else - { // renormalize - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->mAutoResize) - { - panelp->mFractionalSize /= total_fractional_size; - } - } - } -} - -bool LLLayoutStack::animatePanels() -{ - bool continue_animating = false; - - // - // animate visibility - // - for (LLLayoutPanel* panelp : mPanels) - { - if (panelp->getVisible()) - { - if (mAnimate && panelp->mVisibleAmt < 1.f) - { - if (!mAnimatedThisFrame) - { - 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 - { - if (panelp->mVisibleAmt != 1.f) - { - panelp->mVisibleAmt = 1.f; - mAnimatedThisFrame = true; - } - } - } - else // not visible - { - if (mAnimate && panelp->mVisibleAmt > 0.f) - { - if (!mAnimatedThisFrame) - { - 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 - { - if (panelp->mVisibleAmt != 0.f) - { - panelp->mVisibleAmt = 0.f; - mAnimatedThisFrame = true; - } - } - } - - F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f; - if (panelp->mCollapseAmt != collapse_state) - { - if (mAnimate) - { - 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; - } - else - { - panelp->mCollapseAmt = collapse_state; - mAnimatedThisFrame = true; - } - } - } - - 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) // Should replace this when C++20 reverse view adaptor becomes available... - { - if (panelp->mAutoResize) - { - old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim()); - if (panelp->getVisible() && !panelp->mCollapsed) - { - total_visible_fraction += panelp->mFractionalSize; - } - } - - if (panelp == resized_panel) - { - other_resize_panel = following_panel; - } - - if (panelp->getVisible() && !panelp->mCollapsed) - { - following_panel = panelp; - } - } - - if (resized_panel->mAutoResize) - { - if (!other_resize_panel || !other_resize_panel->mAutoResize) - { - delta_auto_resize_headroom += delta_panel_dim; - } - } - else - { - if (!other_resize_panel || other_resize_panel->mAutoResize) - { - delta_auto_resize_headroom -= delta_panel_dim; - } - } - - F32 fraction_given_up = 0.f; - F32 fraction_remaining = 1.f; - F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom; - - enum - { - BEFORE_RESIZED_PANEL, - RESIZED_PANEL, - NEXT_PANEL, - AFTER_RESIZED_PANEL - } which_panel = BEFORE_RESIZED_PANEL; - - for (LLLayoutPanel* panelp : mPanels) - { - if (!panelp->getVisible() || panelp->mCollapsed) - { - if (panelp->mAutoResize) - { - fraction_remaining -= panelp->mFractionalSize; - } - continue; - } - - if (panelp == resized_panel) - { - which_panel = RESIZED_PANEL; - } - - switch(which_panel) - { - 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 - { - // leave non auto-resize panels alone - } - 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) - { - fraction_remaining -= panelp->mFractionalSize; - if (resized_panel->mAutoResize) - { - panelp->mFractionalSize = llclamp(panelp->mFractionalSize + fraction_given_up, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); - fraction_given_up = 0.f; - } - else - { - 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; - } - } - else - { - panelp->mTargetDim -= delta_panel_dim; - } - which_panel = AFTER_RESIZED_PANEL; - break; - case AFTER_RESIZED_PANEL: - if (panelp->mAutoResize && fraction_given_up != 0.f) - { - panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up, - MIN_FRACTIONAL_SIZE, - MAX_FRACTIONAL_SIZE); - } - break; - default: - break; - } - } - updateLayout(); - //normalizeFractionalSizes(); -} - -void LLLayoutStack::reshape(S32 width, S32 height, bool called_from_parent) -{ - mNeedsLayout = true; - LLView::reshape(width, height, called_from_parent); -} - -void LLLayoutStack::updateResizeBarLimits() -{ - LLLayoutPanel* previous_visible_panelp{ nullptr }; - BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available... - { - if (!visible_panelp->getVisible() || visible_panelp->mCollapsed) - { - visible_panelp->mResizeBar->setVisible(false); - continue; - } - - // 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 - { - 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 - { - visible_panelp->mResizeBar->setVisible(false); - } - - previous_visible_panelp = visible_panelp; - } -} - +/**
+ * @file lllayoutstack.cpp
+ * @brief LLLayout class - dynamic stacking of UI elements
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/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 "llcriticaldamp.h"
+#include "lliconctrl.h"
+#include "boost/foreach.hpp"
+
+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");
+
+//
+// LLLayoutPanel
+//
+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())
+ {
+ mVisibleAmt = 0.f;
+ }
+}
+
+void LLLayoutPanel::initFromParams(const Params& p)
+{
+ LLPanel::initFromParams(p);
+ setFollowsNone();
+}
+
+
+LLLayoutPanel::~LLLayoutPanel()
+{
+ // probably not necessary, but...
+ delete mResizeBar;
+ mResizeBar = NULL;
+
+ gFocusMgr.removeKeyboardFocusWithoutCallback(this);
+}
+
+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);
+}
+
+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 && mUserResize && mMinDim == -1)
+ {
+ setMinDim(layout_dim);
+ }
+ mTargetDim = llmax(layout_dim, getMinDim());
+}
+
+void LLLayoutPanel::setVisible( bool visible )
+{
+ if (visible != getVisible())
+ {
+ LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent());
+ if (stackp)
+ {
+ stackp->mNeedsLayout = true;
+ }
+ }
+ LLPanel::setVisible(visible);
+}
+
+void LLLayoutPanel::reshape( S32 width, S32 height, bool called_from_parent /*= true*/ )
+{
+ if (width == getRect().getWidth() && height == getRect().getHeight() && !LLView::sForceReshape) return;
+
+ if (!mIgnoreReshape && !mAutoResize)
+ {
+ 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"),
+ animate("animate", true),
+ clip("clip", true),
+ 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::getInstance()->mSettingGroups["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),
+ drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody"))
+{
+ addSynonym(border_size, "drag_handle_gap");
+}
+
+LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
+: LLView(p),
+ mPanelSpacing(p.border_size),
+ mOrientation(p.orientation),
+ mAnimate(p.animate),
+ 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),
+ mDragHandleColor(p.drag_handle_color())
+{
+}
+
+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();
+
+ // always clip to stack itself
+ LLLocalClipRect clip(getLocalRect());
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if ((!panelp->getVisible() || panelp->mCollapsed)
+ && (panelp->mVisibleAmt < 0.001f || !mAnimate))
+ {
+ // essentially invisible
+ continue;
+ }
+ // clip to layout rectangle, not bounding rectangle
+ LLRect clip_rect = panelp->getRect();
+ // scale clipping rectangle by visible amount
+ if (mOrientation == HORIZONTAL)
+ {
+ clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim();
+ }
+ else
+ {
+ clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim();
+ }
+
+ {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::deleteAllChildren()
+{
+ mPanels.clear();
+ LLView::deleteAllChildren();
+
+ // Not really needed since nothing is left to
+ // display, but for the sake of consistency
+ updateFractionalSizes();
+ mNeedsLayout = true;
+}
+
+void LLLayoutStack::removeChild(LLView* view)
+{
+ LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view));
+
+ if (embedded_panelp)
+ {
+ mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp));
+ LLView::removeChild(view);
+ updateFractionalSizes();
+ mNeedsLayout = true;
+ }
+ else
+ {
+ LLView::removeChild(view);
+ }
+}
+
+bool LLLayoutStack::postBuild()
+{
+ updateLayout();
+ return true;
+}
+
+bool LLLayoutStack::addChild(LLView* child, S32 tab_group)
+{
+ LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child);
+ if (panelp)
+ {
+ panelp->setOrientation(mOrientation);
+ mPanels.push_back(panelp);
+ createResizeBar(panelp);
+ mNeedsLayout = true;
+ }
+ bool result = LLView::addChild(child, tab_group);
+
+ updateFractionalSizes();
+ return result;
+}
+
+void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate)
+{
+ addChild(panel);
+
+ // panel starts off invisible (collapsed)
+ if (animate == ANIMATE)
+ {
+ panel->mVisibleAmt = 0.f;
+ panel->setVisible(true);
+ }
+}
+
+void LLLayoutStack::collapsePanel(LLPanel* panel, bool collapsed)
+{
+ LLLayoutPanel* panel_container = findEmbeddedPanel(panel);
+ if (!panel_container) return;
+
+ panel_container->mCollapsed = collapsed;
+ mNeedsLayout = true;
+}
+
+class LLImagePanel : public LLPanel
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Optional<bool> horizontal;
+ Params() : horizontal("horizontal", false) {}
+ };
+ LLImagePanel(const Params& p) : LLPanel(p), mHorizontal(p.horizontal) {}
+ virtual ~LLImagePanel() {}
+
+ 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();
+ }
+
+private:
+ bool mHorizontal;
+};
+
+void LLLayoutStack::updateLayout()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ 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
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ 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
+ if (!mPanels.empty())
+ {
+ space_to_distribute += ll_round(F32(mPanelSpacing) * mPanels.back()->getVisibleAmount());
+ }
+
+ S32 remaining_space = space_to_distribute;
+ if (space_to_distribute > 0 && total_visible_fraction > 0.f)
+ { // give space proportionally to visible auto resize panels
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction);
+ S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute);
+ panelp->mTargetDim += delta;
+ remaining_space -= delta;
+ }
+ }
+ }
+
+ // distribute any left over pixels to non-collapsed, visible panels
+ for (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();
+
+ for (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
+ {
+ 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));
+
+ if (mOrientation == HORIZONTAL)
+ {
+ cur_pos += panel_visible_dim + panel_spacing;
+
+ if (mShowDragHandle && panel_spacing_round > mDragHandleThickness)
+ {
+ resize_bar_rect.mLeft = panel_rect.mRight + mDragHandleShift;
+ resize_bar_rect.mRight = resize_bar_rect.mLeft + mDragHandleThickness;
+ }
+ else
+ {
+ 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;
+ }
+
+ }
+ else //VERTICAL
+ {
+ 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;
+ }
+ }
+
+ panelp->setIgnoreReshape(true);
+ panelp->setShape(panel_rect);
+ panelp->setIgnoreReshape(false);
+ panelp->mResizeBar->setShape(resize_bar_rect);
+ }
+
+ updateResizeBarLimits();
+
+ // 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
+
+void LLLayoutStack::setPanelSpacing(S32 val)
+{
+ if (mPanelSpacing != val)
+ {
+ mPanelSpacing = val;
+ mNeedsLayout = true;
+ }
+}
+
+LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
+{
+ if (!panelp) return NULL;
+
+ for (LLLayoutPanel* p : mPanels)
+ {
+ if (p == panelp)
+ {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
+{
+ LLLayoutPanel* result = NULL;
+
+ for (LLLayoutPanel* p : mPanels)
+ {
+ if (p->getName() == name)
+ {
+ result = p;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp)
+{
+ for (LLLayoutPanel* lp : mPanels)
+ {
+ 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 = mDragHandleColor;
+ 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)
+ {
+ LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
+ sendChildToFront(resize_barp);
+ }
+}
+
+// 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 (auto& layout : instance_snapshot())
+ {
+ layout.updateLayout();
+ layout.mAnimatedThisFrame = false;
+ }
+}
+
+void LLLayoutStack::updateFractionalSizes()
+{
+ F32 total_resizable_dim = 0.f;
+
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim()));
+ }
+ }
+
+ for (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::normalizeFractionalSizes()
+{
+ S32 num_auto_resize_panels = 0;
+ F32 total_fractional_size = 0.f;
+
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ total_fractional_size += panelp->mFractionalSize;
+ num_auto_resize_panels++;
+ }
+ }
+
+ if (total_fractional_size == 0.f)
+ { // equal distribution
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels;
+ }
+ }
+ }
+ else
+ { // renormalize
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ panelp->mFractionalSize /= total_fractional_size;
+ }
+ }
+ }
+}
+
+bool LLLayoutStack::animatePanels()
+{
+ bool continue_animating = false;
+
+ //
+ // animate visibility
+ //
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->getVisible())
+ {
+ if (mAnimate && panelp->mVisibleAmt < 1.f)
+ {
+ if (!mAnimatedThisFrame)
+ {
+ 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
+ {
+ if (panelp->mVisibleAmt != 1.f)
+ {
+ panelp->mVisibleAmt = 1.f;
+ mAnimatedThisFrame = true;
+ }
+ }
+ }
+ else // not visible
+ {
+ if (mAnimate && panelp->mVisibleAmt > 0.f)
+ {
+ if (!mAnimatedThisFrame)
+ {
+ 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
+ {
+ if (panelp->mVisibleAmt != 0.f)
+ {
+ panelp->mVisibleAmt = 0.f;
+ mAnimatedThisFrame = true;
+ }
+ }
+ }
+
+ F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f;
+ if (panelp->mCollapseAmt != collapse_state)
+ {
+ if (mAnimate)
+ {
+ 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;
+ }
+ else
+ {
+ panelp->mCollapseAmt = collapse_state;
+ mAnimatedThisFrame = true;
+ }
+ }
+ }
+
+ 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) // Should replace this when C++20 reverse view adaptor becomes available...
+ {
+ if (panelp->mAutoResize)
+ {
+ old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim());
+ if (panelp->getVisible() && !panelp->mCollapsed)
+ {
+ total_visible_fraction += panelp->mFractionalSize;
+ }
+ }
+
+ if (panelp == resized_panel)
+ {
+ other_resize_panel = following_panel;
+ }
+
+ if (panelp->getVisible() && !panelp->mCollapsed)
+ {
+ following_panel = panelp;
+ }
+ }
+
+ if (resized_panel->mAutoResize)
+ {
+ if (!other_resize_panel || !other_resize_panel->mAutoResize)
+ {
+ delta_auto_resize_headroom += delta_panel_dim;
+ }
+ }
+ else
+ {
+ if (!other_resize_panel || other_resize_panel->mAutoResize)
+ {
+ delta_auto_resize_headroom -= delta_panel_dim;
+ }
+ }
+
+ F32 fraction_given_up = 0.f;
+ F32 fraction_remaining = 1.f;
+ F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom;
+
+ enum
+ {
+ BEFORE_RESIZED_PANEL,
+ RESIZED_PANEL,
+ NEXT_PANEL,
+ AFTER_RESIZED_PANEL
+ } which_panel = BEFORE_RESIZED_PANEL;
+
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (!panelp->getVisible() || panelp->mCollapsed)
+ {
+ if (panelp->mAutoResize)
+ {
+ fraction_remaining -= panelp->mFractionalSize;
+ }
+ continue;
+ }
+
+ if (panelp == resized_panel)
+ {
+ which_panel = RESIZED_PANEL;
+ }
+
+ switch(which_panel)
+ {
+ 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
+ {
+ // leave non auto-resize panels alone
+ }
+ 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)
+ {
+ fraction_remaining -= panelp->mFractionalSize;
+ if (resized_panel->mAutoResize)
+ {
+ panelp->mFractionalSize = llclamp(panelp->mFractionalSize + fraction_given_up, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE);
+ fraction_given_up = 0.f;
+ }
+ else
+ {
+ 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;
+ }
+ }
+ else
+ {
+ panelp->mTargetDim -= delta_panel_dim;
+ }
+ which_panel = AFTER_RESIZED_PANEL;
+ break;
+ case AFTER_RESIZED_PANEL:
+ if (panelp->mAutoResize && fraction_given_up != 0.f)
+ {
+ panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up,
+ MIN_FRACTIONAL_SIZE,
+ MAX_FRACTIONAL_SIZE);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ updateLayout();
+ //normalizeFractionalSizes();
+}
+
+void LLLayoutStack::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ mNeedsLayout = true;
+ LLView::reshape(width, height, called_from_parent);
+}
+
+void LLLayoutStack::updateResizeBarLimits()
+{
+ LLLayoutPanel* previous_visible_panelp{ nullptr };
+ BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available...
+ {
+ if (!visible_panelp->getVisible() || visible_panelp->mCollapsed)
+ {
+ visible_panelp->mResizeBar->setVisible(false);
+ continue;
+ }
+
+ // 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
+ {
+ 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
+ {
+ visible_panelp->mResizeBar->setVisible(false);
+ }
+
+ previous_visible_panelp = visible_panelp;
+ }
+}
+
|