diff options
Diffstat (limited to 'indra/llui/llview.cpp')
-rw-r--r-- | indra/llui/llview.cpp | 2079 |
1 files changed, 1025 insertions, 1054 deletions
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 2350ea6050..f1b08c380b 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -33,6 +33,7 @@ #include "linden_common.h" +#define LLVIEW_CPP #include "llview.h" #include <cassert> @@ -40,7 +41,6 @@ #include "llrender.h" #include "llevent.h" -#include "llfontgl.h" #include "llfocusmgr.h" #include "llrect.h" #include "llstl.h" @@ -49,6 +49,7 @@ #include "llwindow.h" #include "v3color.h" #include "lluictrlfactory.h" +#include "lltooltip.h" // for ui edit hack #include "llbutton.h" @@ -56,95 +57,106 @@ #include "lltexteditor.h" #include "lltextbox.h" -//HACK: this allows you to instantiate LLView from xml with "<view/>" which we don't want -static LLRegisterWidget<LLView> r("view"); - -BOOL LLView::sDebugRects = FALSE; -BOOL LLView::sDebugKeys = FALSE; S32 LLView::sDepth = 0; -BOOL LLView::sDebugMouseHandling = FALSE; +bool LLView::sDebugRects = false; +bool LLView::sDebugRectsShowNames = true; +bool LLView::sDebugKeys = false; +bool LLView::sDebugMouseHandling = false; std::string LLView::sMouseHandlerMessage; -BOOL LLView::sEditingUI = FALSE; BOOL LLView::sForceReshape = FALSE; -LLView* LLView::sEditingUIView = NULL; +std::set<LLView*> LLView::sPreviewHighlightedElements; +BOOL LLView::sHighlightingDiffs = FALSE; +LLView* LLView::sPreviewClickedElement = NULL; +BOOL LLView::sDrawPreviewHighlights = FALSE; S32 LLView::sLastLeftXML = S32_MIN; S32 LLView::sLastBottomXML = S32_MIN; +std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack; + #if LL_DEBUG BOOL LLView::sIsDrawing = FALSE; #endif -LLView::LLView() : - mParentView(NULL), - mReshapeFlags(FOLLOWS_NONE), - mDefaultTabGroup(0), - mEnabled(TRUE), - mMouseOpaque(TRUE), - mSoundFlags(MOUSE_UP), // default to only make sound on mouse up - mSaveToXML(TRUE), - mIsFocusRoot(FALSE), - mLastVisible(TRUE), - mUseBoundingRect(FALSE), - mVisible(TRUE), - mNextInsertionOrdinal(0), - mHoverCursor(UI_CURSOR_ARROW) -{ -} - -LLView::LLView(const std::string& name, BOOL mouse_opaque) : +// Compiler optimization, generate extern template +template class LLView* LLView::getChild<class LLView>( + const std::string& name, BOOL recurse) const; + +static LLDefaultChildRegistry::Register<LLView> r("view"); + +LLView::Follows::Follows() +: string(""), + flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP) +{} + +LLView::Params::Params() +: name("name", std::string("unnamed")), + enabled("enabled", true), + visible("visible", true), + mouse_opaque("mouse_opaque", true), + follows("follows"), + hover_cursor("hover_cursor", "UI_CURSOR_ARROW"), + use_bounding_rect("use_bounding_rect", false), + tab_group("tab_group", 0), + default_tab_group("default_tab_group"), + tool_tip("tool_tip"), + sound_flags("sound_flags", MOUSE_UP), + layout("layout"), + rect("rect"), + bottom_delta("bottom_delta", S32_MAX), + top_pad("top_pad"), + top_delta("top_delta", S32_MAX), + left_pad("left_pad"), + left_delta("left_delta", S32_MAX), + center_horiz("center_horiz", false), + center_vert("center_vert", false), + from_xui("from_xui", false), + user_resize("user_resize"), + auto_resize("auto_resize"), + needs_translate("translate"), + xmlns("xmlns"), + xmlns_xsi("xmlns:xsi"), + xsi_schemaLocation("xsi:schemaLocation"), + xsi_type("xsi:type") + +{ + addSynonym(rect, ""); +} + +LLView::LLView(const LLView::Params& p) +: mName(p.name), mParentView(NULL), - mName(name), mReshapeFlags(FOLLOWS_NONE), - mDefaultTabGroup(0), - mEnabled(TRUE), - mMouseOpaque(mouse_opaque), - mSoundFlags(MOUSE_UP), // default to only make sound on mouse up - mSaveToXML(TRUE), + mFromXUI(p.from_xui), mIsFocusRoot(FALSE), - mLastVisible(TRUE), - mUseBoundingRect(FALSE), - mVisible(TRUE), + mLastVisible(FALSE), mNextInsertionOrdinal(0), - mHoverCursor(UI_CURSOR_ARROW) -{ -} - - -LLView::LLView( - const std::string& name, const LLRect& rect, BOOL mouse_opaque, U32 reshape) : - mParentView(NULL), - mName(name), - mRect(rect), - mBoundingRect(rect), - mReshapeFlags(reshape), - mDefaultTabGroup(0), - mEnabled(TRUE), - mMouseOpaque(mouse_opaque), - mSoundFlags(MOUSE_UP), // default to only make sound on mouse up - mSaveToXML(TRUE), - mIsFocusRoot(FALSE), - mLastVisible(TRUE), - mUseBoundingRect(FALSE), - mVisible(TRUE), - mNextInsertionOrdinal(0), - mHoverCursor(UI_CURSOR_ARROW) + mHoverCursor(getCursorFromString(p.hover_cursor)), + mEnabled(p.enabled), + mVisible(p.visible), + mMouseOpaque(p.mouse_opaque), + mSoundFlags(p.sound_flags), + mUseBoundingRect(p.use_bounding_rect), + mDefaultTabGroup(p.default_tab_group), + mLastTabGroup(0), + mToolTipMsg((LLStringExplicit)p.tool_tip()), + mDefaultWidgets(NULL) { + // create rect first, as this will supply initial follows flags + setShape(p.rect); + parseFollowsFlags(p); } - LLView::~LLView() { + dirtyRect(); //llinfos << "Deleting view " << mName << ":" << (void*) this << llendl; // llassert(LLView::sIsDrawing == FALSE); - if( gFocusMgr.getKeyboardFocus() == this ) - { - llwarns << "View holding keyboard focus deleted: " << getName() << ". Keyboard focus removed." << llendl; - gFocusMgr.removeKeyboardFocusWithoutCallback( this ); - } - + +// llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators + if( hasMouseCapture() ) { - llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl; + //llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl; gFocusMgr.removeMouseCaptureWithoutCallback( this ); } @@ -155,22 +167,13 @@ LLView::~LLView() mParentView->removeChild(this); } - dispatch_list_t::iterator itor; - for (itor = mDispatchList.begin(); itor != mDispatchList.end(); ++itor) + if (mDefaultWidgets) { - (*itor).second->clearDispatchers(); + std::for_each(mDefaultWidgets->begin(), mDefaultWidgets->end(), + DeletePairedPointer()); + delete mDefaultWidgets; + mDefaultWidgets = NULL; } - - std::for_each(mFloaterControls.begin(), mFloaterControls.end(), - DeletePairedPointer()); - std::for_each(mDummyWidgets.begin(), mDummyWidgets.end(), - DeletePairedPointer()); -} - -// virtual -BOOL LLView::isView() const -{ - return TRUE; } // virtual @@ -185,7 +188,6 @@ BOOL LLView::isPanel() const return FALSE; } -// virtual void LLView::setToolTip(const LLStringExplicit& msg) { mToolTipMsg = msg; @@ -224,27 +226,38 @@ BOOL LLView::getUseBoundingRect() } // virtual -const std::string& LLView::getName() const +std::string LLView::getName() const { - static const std::string unnamed("(no name)"); - return mName.empty() ? unnamed : mName; + return mName.empty() ? std::string("(no name)") : mName; } void LLView::sendChildToFront(LLView* child) { +// llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs if (child && child->getParent() == this) { - mChildList.remove( child ); - mChildList.push_front(child); + // minor optimization, but more importantly, + // won't temporarily create an empty list + if (child != mChildList.front()) + { + mChildList.remove( child ); + mChildList.push_front(child); + } } } void LLView::sendChildToBack(LLView* child) { +// llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs if (child && child->getParent() == this) { - mChildList.remove( child ); - mChildList.push_back(child); + // minor optimization, but more importantly, + // won't temporarily create an empty list + if (child != mChildList.back()) + { + mChildList.remove( child ); + mChildList.push_back(child); + } } } @@ -264,12 +277,18 @@ void LLView::moveChildToBackOfTabGroup(LLUICtrl* child) } } -void LLView::addChild(LLView* child, S32 tab_group) +// virtual +bool LLView::addChild(LLView* child, S32 tab_group) { + if (!child) + { + return false; + } if (mParentView == child) { llerrs << "Adding view " << child->getName() << " as child of itself" << llendl; } + // remove from current parent if (child->mParentView) { @@ -282,55 +301,46 @@ void LLView::addChild(LLView* child, S32 tab_group) // add to ctrl list if is LLUICtrl if (child->isCtrl()) { - // controls are stored in reverse order from render order - addCtrlAtEnd((LLUICtrl*) child, tab_group); + LLUICtrl* ctrl = static_cast<LLUICtrl*>(child); + mCtrlOrder.insert(tab_order_pair_t(ctrl, + tab_order_t(tab_group, mNextInsertionOrdinal))); + + mNextInsertionOrdinal++; } child->mParentView = this; updateBoundingRect(); + mLastTabGroup = tab_group; + return true; } -void LLView::addChildAtEnd(LLView* child, S32 tab_group) +bool LLView::addChildInBack(LLView* child, S32 tab_group) { - if (mParentView == child) - { - llerrs << "Adding view " << child->getName() << " as child of itself" << llendl; - } - // remove from current parent - if (child->mParentView) + if(addChild(child, tab_group)) { - child->mParentView->removeChild(child); + sendChildToBack(child); + return true; } - // add to back of child list - mChildList.push_back(child); - - // add to ctrl list if is LLUICtrl - if (child->isCtrl()) - { - // controls are stored in reverse order from render order - addCtrl((LLUICtrl*) child, tab_group); - } - - child->mParentView = this; - updateBoundingRect(); + return false; } // remove the specified child from the view, and set it's parent to NULL. -void LLView::removeChild(LLView* child, BOOL deleteIt) +void LLView::removeChild(LLView* child) { + //llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs if (child->mParentView == this) { mChildList.remove( child ); child->mParentView = NULL; if (child->isCtrl()) { - removeCtrl((LLUICtrl*)child); - } - if (deleteIt) - { - delete child; + child_tab_order_t::iterator found = mCtrlOrder.find(static_cast<LLUICtrl*>(child)); + if(found != mCtrlOrder.end()) + { + mCtrlOrder.erase(found); + } } } else @@ -340,28 +350,6 @@ void LLView::removeChild(LLView* child, BOOL deleteIt) updateBoundingRect(); } -void LLView::addCtrlAtEnd(LLUICtrl* ctrl, S32 tab_group) -{ - mCtrlOrder.insert(tab_order_pair_t(ctrl, - tab_order_t(tab_group, mNextInsertionOrdinal++))); -} - -void LLView::addCtrl( LLUICtrl* ctrl, S32 tab_group) -{ - // add to front of list by using negative ordinal, which monotonically increases - mCtrlOrder.insert(tab_order_pair_t(ctrl, - tab_order_t(tab_group, -1 * mNextInsertionOrdinal++))); -} - -void LLView::removeCtrl(LLUICtrl* ctrl) -{ - child_tab_order_t::iterator found = mCtrlOrder.find(ctrl); - if(found != mCtrlOrder.end()) - { - mCtrlOrder.erase(found); - } -} - LLView::ctrl_list_t LLView::getCtrlList() const { ctrl_list_t controls; @@ -411,32 +399,28 @@ bool LLCompareByTabOrder::operator() (const LLView* const a, const LLView* const return (a_score == b_score) ? a < b : a_score < b_score; } -BOOL LLView::isInVisibleChain() const +bool LLView::trueToRoot(const boost::function<bool (const LLView*)>& predicate) const { const LLView* cur_view = this; while(cur_view) { - if (!cur_view->getVisible()) + if(!predicate(cur_view)) { - return FALSE; + return false; } cur_view = cur_view->getParent(); } - return TRUE; + return true; +} + +BOOL LLView::isInVisibleChain() const +{ + return trueToRoot(&LLView::getVisible); } BOOL LLView::isInEnabledChain() const { - const LLView* cur_view = this; - while(cur_view) - { - if (!cur_view->getEnabled()) - { - return FALSE; - } - cur_view = cur_view->getParent(); - } - return TRUE; + return trueToRoot(&LLView::getEnabled); } // virtual @@ -463,31 +447,33 @@ void LLView::setEnabled(BOOL enabled) } //virtual -BOOL LLView::setLabelArg( const std::string& key, const LLStringExplicit& text ) +bool LLView::isAvailable() const { - return FALSE; + return isInEnabledChain() && isInVisibleChain(); } -//virtual -LLRect LLView::getSnapRect() const +//static +bool LLView::isAvailable(const LLView* view) { - return mRect; + return view && view->isAvailable(); } //virtual -LLRect LLView::getRequiredRect() +BOOL LLView::setLabelArg( const std::string& key, const LLStringExplicit& text ) { - return mRect; + return FALSE; } //virtual -void LLView::onFocusLost() +LLRect LLView::getSnapRect() const { + return mRect; } //virtual -void LLView::onFocusReceived() +LLRect LLView::getRequiredRect() { + return mRect; } BOOL LLView::focusNextRoot() @@ -617,14 +603,15 @@ void LLView::setVisible(BOOL visible) if (!getParent() || getParent()->isInVisibleChain()) { // tell all children of this view that the visibility may have changed - onVisibilityChange( visible ); + dirtyRect(); + handleVisibilityChange( visible ); } updateBoundingRect(); } } // virtual -void LLView::onVisibilityChange ( BOOL new_visibility ) +void LLView::handleVisibilityChange ( BOOL new_visibility ) { for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { @@ -632,7 +619,7 @@ void LLView::onVisibilityChange ( BOOL new_visibility ) // only views that are themselves visible will have their overall visibility affected by their ancestors if (viewp->getVisible()) { - viewp->onVisibilityChange ( new_visibility ); + viewp->handleVisibilityChange ( new_visibility ); } } } @@ -651,119 +638,111 @@ BOOL LLView::canSnapTo(const LLView* other_view) } // virtual -void LLView::snappedTo(const LLView* snap_view) +void LLView::setSnappedTo(const LLView* snap_view) { } BOOL LLView::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleHover( x, y, mask ) != NULL; - if( !handled - && blockMouseEvent(x, y) ) - { - LLUI::sWindow->setCursor(mHoverCursor); - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - handled = TRUE; - } + return childrenHandleHover( x, y, mask ) != NULL; +} - return handled; +void LLView::onMouseEnter(S32 x, S32 y, MASK mask) +{ + //llinfos << "Mouse entered " << getName() << llendl; } -std::string LLView::getShowNamesToolTip() +void LLView::onMouseLeave(S32 x, S32 y, MASK mask) { - LLView* view = getParent(); - std::string name; - std::string tool_tip = mName; + //llinfos << "Mouse left " << getName() << llendl; +} - while (view) - { - name = view->getName(); - if (name == "root") break; +LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask) +{ + LLView* handled_view = NULL; + for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + { + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if(!viewp->pointInView(local_x, local_y) + || !viewp->getVisible()) + { + continue; + } - if (view->getToolTip().find(".xml") != std::string::npos) + if (viewp->handleToolTip(local_x, local_y, mask) ) { - tool_tip = view->getToolTip() + "/" + tool_tip; + if (sDebugMouseHandling) + { + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; + } + + handled_view = viewp; break; } - else + + if (viewp->blockMouseEvent(local_x, local_y)) { - tool_tip = view->getName() + "/" + tool_tip; + handled_view = viewp; + break; } - - view = view->getParent(); } - - return "/" + tool_tip; + return handled_view; } -BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen) +LLView* LLView::childFromPoint(S32 x, S32 y) { - BOOL handled = FALSE; - - std::string tool_tip; - + if (!getVisible() ) + return false; for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { LLView* viewp = *child_it; - S32 local_x = x - viewp->mRect.mLeft; - S32 local_y = y - viewp->mRect.mBottom; - // Allow tooltips for disabled views so we can explain to the user why - // the view is disabled. JC - if( viewp->pointInView(local_x, local_y) - && viewp->getVisible() - // && viewp->getEnabled() - && viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen )) - { - // child provided a tooltip, just return - if (!msg.empty()) return TRUE; - - // otherwise, one of our children ate the event so don't traverse - // siblings however, our child did not actually provide a tooltip - // so we might want to - handled = TRUE; - break; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() ) + { + continue; } - } + return viewp; - // get our own tooltip - tool_tip = mToolTipMsg.getString(); - if ( - LLUI::sShowXUINames - && (tool_tip.find(".xml", 0) == std::string::npos) - && (mName.find("Drag", 0) == std::string::npos)) - { - tool_tip = getShowNamesToolTip(); } + return 0; +} - BOOL show_names_text_box = LLUI::sShowXUINames && dynamic_cast<LLTextBox*>(this) != NULL; +BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) +{ + BOOL handled = FALSE; - // don't allow any siblings to handle this event - // even if we don't have a tooltip - if (getMouseOpaque() || show_names_text_box) + // parents provide tooltips first, which are optionally + // overridden by children, in case child is mouse_opaque + if (!mToolTipMsg.empty()) { + // allow "scrubbing" over ui by showing next tooltip immediately + // if previous one was still visible + F32 timeout = LLToolTipMgr::instance().toolTipVisible() + ? 0.f + : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" ); + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(mToolTipMsg) + .sticky_rect(calcScreenRect()) + .delay_time(timeout)); + handled = TRUE; } - if(!tool_tip.empty()) + // child tooltips will override our own + LLView* child_handler = childrenHandleToolTip(x, y, mask); + if (child_handler) { - msg = tool_tip; - - // Convert rect local to screen coordinates - localPointToScreen( - 0, 0, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - localPointToScreen( - mRect.getWidth(), mRect.getHeight(), - &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) ); - handled = TRUE; } return handled; } - BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent) { BOOL handled = FALSE; @@ -844,20 +823,7 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* accept, std::string& tooltip_msg) { - // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent - BOOL handled = childrenHandleDragAndDrop( x, y, mask, drop, - cargo_type, - cargo_data, - accept, - tooltip_msg) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - *accept = ACCEPT_NO; - handled = TRUE; - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLView " << getName() << llendl; - } - - return handled; + return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL; } LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, @@ -867,28 +833,33 @@ LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, EAcceptance* accept, std::string& tooltip_msg) { - LLView* handled_view = FALSE; - // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent - if( getVisible() ) -// if( getVisible() && getEnabled() ) + LLView* handled_view = NULL; + for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { - for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if( !viewp->pointInView(local_x, local_y) || + !viewp->getVisible() || + !viewp->getEnabled()) { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if( viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleDragAndDrop(local_x, local_y, mask, drop, - cargo_type, - cargo_data, - accept, - tooltip_msg)) - { - handled_view = viewp; - break; - } + continue; + } + if (viewp->handleDragAndDrop(local_x, local_y, mask, drop, + cargo_type, + cargo_data, + accept, + tooltip_msg)) + { + handled_view = viewp; + break; + } + + if (viewp->blockMouseEvent(x, y)) + { + *accept = ACCEPT_NO; + handled_view = viewp; + break; } } return handled_view; @@ -905,110 +876,42 @@ BOOL LLView::hasMouseCapture() BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleMouseUp( x, y, mask ) != NULL; } BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) { - LLView* handled_view = childrenHandleMouseDown( x, y, mask ); - BOOL handled = (handled_view != NULL); - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - handled_view = this; - } - - // HACK If we're editing UI, select the leaf view that ate the click. - if (sEditingUI && handled_view) - { - // need to find leaf views, big hack - LLButton* buttonp = dynamic_cast<LLButton*>(handled_view); - LLLineEditor* line_editorp = dynamic_cast<LLLineEditor*>(handled_view); - LLTextEditor* text_editorp = dynamic_cast<LLTextEditor*>(handled_view); - LLTextBox* text_boxp = dynamic_cast<LLTextBox*>(handled_view); - if (buttonp - || line_editorp - || text_editorp - || text_boxp) - { - sEditingUIView = handled_view; - } - } - - return handled; + return childrenHandleMouseDown( x, y, mask ) != NULL; } BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handleMouseDown(x, y, mask); - handled = TRUE; - } - return handled; + return childrenHandleDoubleClick( x, y, mask ) != NULL; } BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) { - BOOL handled = FALSE; - if( getVisible() && getEnabled() ) - { - handled = childrenHandleScrollWheel( x, y, clicks ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - } - return handled; + return childrenHandleScrollWheel( x, y, clicks ) != NULL; } BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleRightMouseDown( x, y, mask ) != NULL; } BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleRightMouseUp( x, y, mask ) != NULL; } BOOL LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - LLView* handled_view = childrenHandleMiddleMouseDown( x, y, mask ); - BOOL handled = (handled_view != NULL); - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - handled_view = this; - } - - return handled; + return childrenHandleMiddleMouseDown( x, y, mask ) != NULL; } BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMiddleMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleMiddleMouseUp( x, y, mask ) != NULL; } @@ -1022,14 +925,18 @@ LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) - && viewp->getVisible() - && viewp->getEnabled() - && viewp->handleScrollWheel( local_x, local_y, clicks )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if (viewp->handleScrollWheel( local_x, local_y, clicks )) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } handled_view = viewp; @@ -1050,19 +957,31 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if(viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleHover(local_x, local_y, mask) ) + if(!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if (viewp->handleHover(local_x, local_y, mask) ) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + LLUI::sWindow->setCursor(viewp->getHoverCursor()); + + handled_view = viewp; + break; + } } } return handled_view; @@ -1128,18 +1047,28 @@ LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask) S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleMouseDown( local_x, local_y, mask )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if(viewp->handleMouseDown( local_x, local_y, mask )) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } handled_view = viewp; break; } + + if(viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } return handled_view; } @@ -1155,15 +1084,27 @@ LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleRightMouseDown( local_x, local_y, mask )) + + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if (viewp->handleRightMouseDown( local_x, local_y, mask )) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } + + handled_view = viewp; + break; + } + + if (viewp->blockMouseEvent(local_x, local_y)) + { handled_view = viewp; break; } @@ -1183,18 +1124,28 @@ LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleMiddleMouseDown( local_x, local_y, mask )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if(viewp->handleMiddleMouseDown( local_x, local_y, mask )) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1211,18 +1162,29 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleDoubleClick( local_x, local_y, mask )) + + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if (viewp->handleDoubleClick( local_x, local_y, mask )) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1238,21 +1200,28 @@ LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (!viewp->pointInView(local_x, local_y)) - continue; - if (!viewp->getVisible()) - continue; - if (!viewp->getEnabled()) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { continue; + } + if (viewp->handleMouseUp( local_x, local_y, mask )) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1268,18 +1237,28 @@ LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleRightMouseUp( local_x, local_y, mask )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled() ) + { + continue; + } + + if(viewp->handleRightMouseUp( local_x, local_y, mask )) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } handled_view = viewp; break; } + + if(viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1295,18 +1274,28 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleMiddleMouseUp( local_x, local_y, mask )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if(viewp->handleMiddleMouseUp( local_x, local_y, mask )) { if (sDebugMouseHandling) { - sMouseHandlerMessage = std::string("->") + viewp->mName + sMouseHandlerMessage; + sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage; } handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1314,67 +1303,77 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) void LLView::draw() { - if (sDebugRects) - { - drawDebugRect(); - - // Check for bogus rectangle - if (getRect().mRight <= getRect().mLeft - || getRect().mTop <= getRect().mBottom) - { - llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl; - } - } - - LLRect rootRect = getRootView()->getRect(); - LLRect screenRect; + drawChildren(); +} - // draw focused control on top of everything else - LLView* focus_view = gFocusMgr.getKeyboardFocus(); - if (focus_view && focus_view->getParent() != this) +void LLView::drawChildren() +{ + if (!mChildList.empty()) { - focus_view = NULL; - } + LLRect rootRect = getRootView()->getRect(); + LLRect screenRect; - ++sDepth; - for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter) - { - LLView *viewp = *child_iter; + ++sDepth; - if (viewp->getVisible() && viewp != focus_view && viewp->getRect().isValid()) + for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter) { - // Only draw views that are within the root view - localRectToScreen(viewp->getRect(),&screenRect); - if ( rootRect.rectInRect(&screenRect) ) + child_list_reverse_iter_t child = child_iter++; + LLView *viewp = *child; + + if (viewp->getVisible() && viewp->getRect().isValid()) { - glMatrixMode(GL_MODELVIEW); - LLUI::pushMatrix(); + // Only draw views that are within the root view + localRectToScreen(viewp->getRect(),&screenRect); + if ( rootRect.overlaps(screenRect) && LLUI::sDirtyRect.overlaps(screenRect)) { - LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f); - viewp->draw(); + glMatrixMode(GL_MODELVIEW); + LLUI::pushMatrix(); + { + LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f); + viewp->draw(); + + if (sDebugRects) + { + viewp->drawDebugRect(); + + // Check for bogus rectangle + if (!getRect().isValid()) + { + llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl; + } + } + } + LLUI::popMatrix(); } - LLUI::popMatrix(); } - } + } + --sDepth; } - --sDepth; - if (focus_view && focus_view->getVisible()) - { - drawChild(focus_view); - } + gGL.getTexUnit(0)->disable(); +} - // HACK - if (sEditingUI && this == sEditingUIView) - { - drawDebugRect(); +void LLView::dirtyRect() +{ + LLView* child = getParent(); + LLView* parent = child ? child->getParent() : NULL; + LLView* cur = this; + while (child && parent && parent->getParent()) + { //find third to top-most view + cur = child; + child = parent; + parent = parent->getParent(); } + + LLUI::dirtyRect(cur->calcScreenRect()); } //Draw a box for debugging. void LLView::drawDebugRect() { + std::set<LLView*>::iterator preview_iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); // figure out if it's a previewed element + LLUI::pushMatrix(); { // drawing solids requires texturing be disabled @@ -1388,10 +1387,18 @@ void LLView::drawDebugRect() LLRect debug_rect = mUseBoundingRect ? mBoundingRect : mRect; // draw red rectangle for the border - LLColor4 border_color(0.f, 0.f, 0.f, 1.f); - if (sEditingUI) + LLColor4 border_color(0.25f, 0.25f, 0.25f, 1.f); + if(preview_iter != sPreviewHighlightedElements.end()) { - border_color.mV[0] = 1.f; + if(LLView::sPreviewClickedElement && this == sPreviewClickedElement) + { + border_color = LLColor4::red; + } + else + { + static LLUIColor scroll_highlighted_color = LLUIColorTable::instance().getColor("ScrollHighlightedColor"); + border_color = scroll_highlighted_color; + } } else { @@ -1414,8 +1421,10 @@ void LLView::drawDebugRect() gGL.vertex2i(0, debug_rect.getHeight() - 1); gGL.end(); - // Draw the name if it's not a leaf node - if (mChildList.size() && !sEditingUI) + // Draw the name if it's not a leaf node or not in editing or preview mode + if (mChildList.size() + && preview_iter == sPreviewHighlightedElements.end() + && sDebugRectsShowNames) { //char temp[256]; S32 x, y; @@ -1425,7 +1434,7 @@ void LLView::drawDebugRect() std::string debug_text = llformat("%s (%d x %d)", getName().c_str(), debug_rect.getWidth(), debug_rect.getHeight()); LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, - LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, + LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); } } @@ -1529,45 +1538,53 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent) updateBoundingRect(); } -void LLView::updateBoundingRect() +LLRect LLView::calcBoundingRect() { - if (isDead()) return; + LLRect local_bounding_rect = LLRect::null; - if (mUseBoundingRect) + child_list_const_iter_t child_it; + for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { - LLRect local_bounding_rect = LLRect::null; - - child_list_const_iter_t child_it; - for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + LLView* childp = *child_it; + // ignore invisible and "top" children when calculating bounding rect + // such as combobox popups + if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl()) { - LLView* childp = *child_it; - // ignore invisible and "top" children when calculating bounding rect - // such as combobox popups - if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl()) - { - continue; - } + continue; + } - LLRect child_bounding_rect = childp->getBoundingRect(); + LLRect child_bounding_rect = childp->getBoundingRect(); - if (local_bounding_rect.isNull()) - { - // start out with bounding rect equal to first visible child's bounding rect - local_bounding_rect = child_bounding_rect; - } - else + if (local_bounding_rect.isEmpty()) + { + // start out with bounding rect equal to first visible child's bounding rect + local_bounding_rect = child_bounding_rect; + } + else + { + // accumulate non-null children rectangles + if (!child_bounding_rect.isEmpty()) { - // accumulate non-null children rectangles - if (!child_bounding_rect.isNull()) - { - local_bounding_rect.unionWith(child_bounding_rect); - } + local_bounding_rect.unionWith(child_bounding_rect); } } + } + + // convert to parent-relative coordinates + local_bounding_rect.translate(mRect.mLeft, mRect.mBottom); + return local_bounding_rect; +} + - mBoundingRect = local_bounding_rect; - // translate into parent-relative coordinates - mBoundingRect.translate(mRect.mLeft, mRect.mBottom); +void LLView::updateBoundingRect() +{ + if (isDead()) return; + + LLRect cur_rect = mBoundingRect; + + if (mUseBoundingRect) + { + mBoundingRect = calcBoundingRect(); } else { @@ -1579,17 +1596,36 @@ void LLView::updateBoundingRect() { getParent()->updateBoundingRect(); } + + if (mBoundingRect != cur_rect) + { + dirtyRect(); + } + } -LLRect LLView::getScreenRect() const +LLRect LLView::calcScreenRect() const { - // *FIX: check for one-off error LLRect screen_rect; localPointToScreen(0, 0, &screen_rect.mLeft, &screen_rect.mBottom); localPointToScreen(getRect().getWidth(), getRect().getHeight(), &screen_rect.mRight, &screen_rect.mTop); return screen_rect; } +LLRect LLView::calcScreenBoundingRect() const +{ + LLRect screen_rect; + // get bounding rect, if used + LLRect bounding_rect = mUseBoundingRect ? mBoundingRect : mRect; + + // convert to local coordinates, as defined by mRect + bounding_rect.translate(-mRect.mLeft, -mRect.mBottom); + + localPointToScreen(bounding_rect.mLeft, bounding_rect.mBottom, &screen_rect.mLeft, &screen_rect.mBottom); + localPointToScreen(bounding_rect.mRight, bounding_rect.mTop, &screen_rect.mRight, &screen_rect.mTop); + return screen_rect; +} + LLRect LLView::getLocalBoundingRect() const { LLRect local_bounding_rect = getBoundingRect(); @@ -1636,7 +1672,7 @@ BOOL LLView::hasAncestor(const LLView* parentp) const BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const { - LLView *child = getChildView(childname, TRUE, FALSE); + LLView *child = findChildView(childname, TRUE); if (child) { return gFocusMgr.childHasKeyboardFocus(child); @@ -1651,14 +1687,31 @@ BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const { - return getChildView(childname, recurse, FALSE) != NULL; + return findChildView(childname, recurse) != NULL; } //----------------------------------------------------------------------------- // getChildView() //----------------------------------------------------------------------------- -LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const +LLView* LLView::getChildView(const std::string& name, BOOL recurse) const +{ + LLView* child = findChildView(name, recurse); + if (!child) + { + child = getDefaultWidget<LLView>(name); + if (!child) + { + child = LLUICtrlFactory::createDefaultWidget<LLView>(name); + } + } + return child; +} + +static LLFastTimer::DeclareTimer FTM_FIND_VIEWS("Find Widgets"); + +LLView* LLView::findChildView(const std::string& name, BOOL recurse) const { + LLFastTimer ft(FTM_FIND_VIEWS); //richard: should we allow empty names? //if(name.empty()) // return NULL; @@ -1678,18 +1731,13 @@ LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_ for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { LLView* childp = *child_it; - LLView* viewp = childp->getChildView(name, recurse, FALSE); + LLView* viewp = childp->findChildView(name, recurse); if ( viewp ) { return viewp; } } } - - if (create_if_missing) - { - return createDummyWidget<LLView>(name); - } return NULL; } @@ -1777,82 +1825,152 @@ LLView* LLView::getRootView() return view; } -BOOL LLView::deleteViewByHandle(LLHandle<LLView> handle) +LLView* LLView::findPrevSibling(LLView* child) +{ + child_list_t::iterator prev_it = std::find(mChildList.begin(), mChildList.end(), child); + if (prev_it != mChildList.end() && prev_it != mChildList.begin()) + { + return *(--prev_it); + } + return NULL; +} + +LLView* LLView::findNextSibling(LLView* child) +{ + child_list_t::iterator next_it = std::find(mChildList.begin(), mChildList.end(), child); + if (next_it != mChildList.end()) + { + next_it++; + } + + return (next_it != mChildList.end()) ? *next_it : NULL; +} + +void LLView::deleteViewByHandle(LLHandle<LLView> handle) { LLView* viewp = handle.get(); delete viewp; - return viewp != NULL; } -// Moves the view so that it is entirely inside of constraint. -// If the view will not fit because it's too big, aligns with the top and left. -// (Why top and left? That's where the drag bars are for floaters.) -BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outside ) +LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, BOOL allow_partial_outside) { - S32 delta_x = 0; - S32 delta_y = 0; + LLCoordGL delta; if (allow_partial_outside) { const S32 KEEP_ONSCREEN_PIXELS = 16; - if( getRect().mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft ) + if( input.mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft ) { - delta_x = constraint.mLeft - (getRect().mRight - KEEP_ONSCREEN_PIXELS); + delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS); } else - if( getRect().mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight ) + if( input.mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight ) { - delta_x = constraint.mRight - (getRect().mLeft + KEEP_ONSCREEN_PIXELS); + delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS); } - if( getRect().mTop > constraint.mTop ) + if( input.mTop > constraint.mTop ) { - delta_y = constraint.mTop - getRect().mTop; + delta.mY = constraint.mTop - input.mTop; } else - if( getRect().mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom ) + if( input.mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom ) { - delta_y = constraint.mBottom - (getRect().mTop - KEEP_ONSCREEN_PIXELS); + delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS); } } else { - if( getRect().mLeft < constraint.mLeft ) + if( input.mLeft < constraint.mLeft ) { - delta_x = constraint.mLeft - getRect().mLeft; + delta.mX = constraint.mLeft - input.mLeft; } else - if( getRect().mRight > constraint.mRight ) + if( input.mRight > constraint.mRight ) { - delta_x = constraint.mRight - getRect().mRight; + delta.mX = constraint.mRight - input.mRight; // compensate for left edge possible going off screen - delta_x += llmax( 0, getRect().getWidth() - constraint.getWidth() ); + delta.mX += llmax( 0, input.getWidth() - constraint.getWidth() ); } - if( getRect().mTop > constraint.mTop ) + if( input.mTop > constraint.mTop ) { - delta_y = constraint.mTop - getRect().mTop; + delta.mY = constraint.mTop - input.mTop; } else - if( getRect().mBottom < constraint.mBottom ) + if( input.mBottom < constraint.mBottom ) { - delta_y = constraint.mBottom - getRect().mBottom; + delta.mY = constraint.mBottom - input.mBottom; // compensate for top edge possible going off screen - delta_y -= llmax( 0, getRect().getHeight() - constraint.getHeight() ); + delta.mY -= llmax( 0, input.getHeight() - constraint.getHeight() ); } } - if (delta_x != 0 || delta_y != 0) + return delta; +} + +// Moves the view so that it is entirely inside of constraint. +// If the view will not fit because it's too big, aligns with the top and left. +// (Why top and left? That's where the drag bars are for floaters.) +BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outside ) +{ + LLCoordGL translation = getNeededTranslation(getRect(), constraint, allow_partial_outside); + + if (translation.mX != 0 || translation.mY != 0) + { + translate(translation.mX, translation.mY); + return TRUE; + } + return FALSE; +} + +// move this view into "inside" but not onto "exclude" +// NOTE: if this view is already contained in "inside", we ignore the "exclude" rect +BOOL LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside ) +{ + LLCoordGL translation = getNeededTranslation(getRect(), inside, allow_partial_outside); + + if (translation.mX != 0 || translation.mY != 0) { - translate(delta_x, delta_y); + // translate ourselves into constraint rect + translate(translation.mX, translation.mY); + + // do we overlap with exclusion area? + // keep moving in the same direction to the other side of the exclusion rect + if (exclude.overlaps(getRect())) + { + // moving right + if (translation.mX > 0) + { + translate(exclude.mRight - getRect().mLeft, 0); + } + // moving left + else if (translation.mX < 0) + { + translate(exclude.mLeft - getRect().mRight, 0); + } + + // moving up + if (translation.mY > 0) + { + translate(0, exclude.mTop - getRect().mBottom); + } + // moving down + else if (translation.mY < 0) + { + translate(0, exclude.mBottom - getRect().mTop); + } + } + return TRUE; } return FALSE; } + void LLView::centerWithin(const LLRect& bounds) { S32 left = bounds.mLeft + (bounds.getWidth() - getRect().getWidth()) / 2; @@ -1943,132 +2061,6 @@ BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, LLView* o return FALSE; } -// virtual -LLXMLNodePtr LLView::getXML(bool save_children) const -{ - //FIXME: need to provide actual derived type tag, probably outside this method - LLXMLNodePtr node = new LLXMLNode("view", FALSE); - - node->createChild("name", TRUE)->setStringValue(getName()); - node->createChild("width", TRUE)->setIntValue(getRect().getWidth()); - node->createChild("height", TRUE)->setIntValue(getRect().getHeight()); - - LLView* parent = getParent(); - S32 left = getRect().mLeft; - S32 bottom = getRect().mBottom; - if (parent) bottom -= parent->getRect().getHeight(); - - node->createChild("left", TRUE)->setIntValue(left); - node->createChild("bottom", TRUE)->setIntValue(bottom); - - U32 follows_flags = getFollows(); - if (follows_flags) - { - std::stringstream buffer; - bool pipe = false; - if (followsLeft()) - { - buffer << "left"; - pipe = true; - } - if (followsTop()) - { - if (pipe) buffer << "|"; - buffer << "top"; - pipe = true; - } - if (followsRight()) - { - if (pipe) buffer << "|"; - buffer << "right"; - pipe = true; - } - if (followsBottom()) - { - if (pipe) buffer << "|"; - buffer << "bottom"; - } - node->createChild("follows", TRUE)->setStringValue(buffer.str()); - } - // Export all widgets as enabled and visible - code must disable. - node->createChild("mouse_opaque", TRUE)->setBoolValue(mMouseOpaque ); - if (!mToolTipMsg.getString().empty()) - { - node->createChild("tool_tip", TRUE)->setStringValue(mToolTipMsg.getString()); - } - if (mSoundFlags != MOUSE_UP) - { - node->createChild("sound_flags", TRUE)->setIntValue((S32)mSoundFlags); - } - - node->createChild("enabled", TRUE)->setBoolValue(getEnabled()); - - if (!mControlName.empty()) - { - node->createChild("control_name", TRUE)->setStringValue(mControlName); - } - return node; -} - -//static -LLView* LLView::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) -{ - LLView* viewp = new LLView(); - viewp->initFromXML(node, parent); - return viewp; -} - -// static -void LLView::addColorXML(LLXMLNodePtr node, const LLColor4& color, - const char* xml_name, const char* control_name) -{ - if (color != LLUI::sColorsGroup->getColor(ll_safe_string(control_name))) - { - node->createChild(xml_name, TRUE)->setFloatValue(4, color.mV); - } -} - -//static -std::string LLView::escapeXML(const std::string& xml, std::string& indent) -{ - std::string ret = indent + "\"" + LLXMLNode::escapeXML(xml); - - //replace every newline with a close quote, new line, indent, open quote - size_t index = ret.size()-1; - size_t fnd; - - while ((fnd = ret.rfind("\n", index)) != std::string::npos) - { - ret.replace(fnd, 1, "\"\n" + indent + "\""); - index = fnd-1; - } - - //append close quote - ret.append("\""); - - return ret; -} - -// static -LLWString LLView::escapeXML(const LLWString& xml) -{ - LLWString out; - for (LLWString::size_type i = 0; i < xml.size(); ++i) - { - llwchar c = xml[i]; - switch(c) - { - case '"': out.append(utf8string_to_wstring(""")); break; - case '\'': out.append(utf8string_to_wstring("'")); break; - case '&': out.append(utf8string_to_wstring("&")); break; - case '<': out.append(utf8string_to_wstring("<")); break; - case '>': out.append(utf8string_to_wstring(">")); break; - default: out.push_back(c); break; - } - } - return out; -} - // static const LLCtrlQuery & LLView::getTabOrderQuery() { @@ -2105,7 +2097,12 @@ const LLCtrlQuery & LLView::getFocusRootsQuery() } -void LLView::userSetShape(const LLRect& new_rect) +void LLView::setShape(const LLRect& new_rect, bool by_user) +{ + handleReshape(new_rect, by_user); +} + +void LLView::handleReshape(const LLRect& new_rect, bool by_user) { reshape(new_rect.getWidth(), new_rect.getHeight()); translate(new_rect.mLeft - getRect().mLeft, new_rect.mBottom - getRect().mBottom); @@ -2353,560 +2350,534 @@ LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESna // Listener dispatch functions //----------------------------------------------------------------------------- -void LLView::registerEventListener(std::string name, LLSimpleListener* function) -{ - mDispatchList.insert(std::pair<std::string, LLSimpleListener*>(name, function)); -} - -void LLView::deregisterEventListener(std::string name) -{ - dispatch_list_t::iterator itor = mDispatchList.find(name); - if (itor != mDispatchList.end()) - { - mDispatchList.erase(itor); - } -} -std::string LLView::findEventListener(LLSimpleListener *listener) const +LLControlVariable *LLView::findControl(const std::string& name) { - dispatch_list_t::const_iterator itor; - for (itor = mDispatchList.begin(); itor != mDispatchList.end(); ++itor) + // parse the name to locate which group it belongs to + std::size_t key_pos= name.find("."); + if(key_pos!= std::string::npos ) { - if (itor->second == listener) + std::string control_group_key = name.substr(0, key_pos); + LLControlVariable* control; + // check if it's in the control group that name indicated + if(LLUI::sSettingGroups[control_group_key]) { - return itor->first; + control = LLUI::sSettingGroups[control_group_key]->getControl(name); + if (control) + { + return control; + } } } - if (mParentView) - { - return mParentView->findEventListener(listener); - } - return LLStringUtil::null; + + LLControlGroup& control_group = LLUI::getControlControlGroup(name); + return control_group.getControl(name); } -LLSimpleListener* LLView::getListenerByName(const std::string& callback_name) +const S32 FLOATER_H_MARGIN = 15; +const S32 MIN_WIDGET_HEIGHT = 10; +const S32 VPAD = 4; + +void LLView::initFromParams(const LLView::Params& params) { - LLSimpleListener* callback = NULL; - dispatch_list_t::iterator itor = mDispatchList.find(callback_name); - if (itor != mDispatchList.end()) - { - callback = itor->second; - } - else if (mParentView) + LLRect required_rect = getRequiredRect(); + + S32 width = llmax(getRect().getWidth(), required_rect.getWidth()); + S32 height = llmax(getRect().getHeight(), required_rect.getHeight()); + + reshape(width, height); + + // call virtual methods with most recent data + // use getters because these values might not come through parameter block + setEnabled(getEnabled()); + setVisible(getVisible()); + + if (!params.name().empty()) { - callback = mParentView->getListenerByName(callback_name); + setName(params.name()); } - return callback; + + mLayout = params.layout(); } -LLControlVariable *LLView::findControl(const std::string& name) +void LLView::parseFollowsFlags(const LLView::Params& params) { - control_map_t::iterator itor = mFloaterControls.find(name); - if (itor != mFloaterControls.end()) + // preserve follows flags set by code if user did not override + if (!params.follows.isProvided()) { - return itor->second; + return; } - if (mParentView) + + // interpret either string or bitfield version of follows + if (params.follows.string.isChosen()) { - return mParentView->findControl(name); - } - return LLUI::sConfigGroup->getControl(name); -} + setFollows(FOLLOWS_NONE); -const S32 FLOATER_H_MARGIN = 15; -const S32 MIN_WIDGET_HEIGHT = 10; -const S32 VPAD = 4; + std::string follows = params.follows.string; -// static -U32 LLView::createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect) -{ - U32 follows = 0; - S32 x = rect.mLeft; - S32 y = rect.mBottom; - S32 w = rect.getWidth(); - S32 h = rect.getHeight(); + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("|"); + tokenizer tokens(follows, sep); + tokenizer::iterator token_iter = tokens.begin(); - U32 last_x = 0; - U32 last_y = 0; - if (parent_view) - { - last_y = parent_view->getRect().getHeight(); - child_list_t::const_iterator itor = parent_view->getChildList()->begin(); - if (itor != parent_view->getChildList()->end()) + while(token_iter != tokens.end()) { - LLView *last_view = (*itor); - if (last_view->getSaveToXML()) + const std::string& token_str = *token_iter; + if (token_str == "left") { - last_x = last_view->getRect().mLeft; - last_y = last_view->getRect().mBottom; + setFollowsLeft(); } + else if (token_str == "right") + { + setFollowsRight(); + } + else if (token_str == "top") + { + setFollowsTop(); + } + else if (token_str == "bottom") + { + setFollowsBottom(); + } + else if (token_str == "all") + { + setFollowsAll(); + } + ++token_iter; } } - - std::string rect_control; - node->getAttributeString("rect_control", rect_control); - if (! rect_control.empty()) + else if (params.follows.flags.isChosen()) { - LLRect rect = LLUI::sConfigGroup->getRect(rect_control); - x = rect.mLeft; - y = rect.mBottom; - w = rect.getWidth(); - h = rect.getHeight(); - } - - if (node->hasAttribute("left")) - { - node->getAttributeS32("left", x); - } - if (node->hasAttribute("bottom")) - { - node->getAttributeS32("bottom", y); + setFollows(params.follows.flags); } +} - // Make your width the width of the containing - // view if you don't specify a width. - if (parent_view) - { - if(w == 0) - { - w = llmax(required_rect.getWidth(), parent_view->getRect().getWidth() - (FLOATER_H_MARGIN) - x); - } - if(h == 0) - { - h = llmax(MIN_WIDGET_HEIGHT, required_rect.getHeight()); +// static +//LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node) +//{ +// LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT; +// +// if (node->hasAttribute("halign")) +// { +// std::string horizontal_align_name; +// node->getAttributeString("halign", horizontal_align_name); +// gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name); +// } +// return gl_hfont_align; +//} + +// Return the rectangle of the last-constructed child, +// if present and a first-class widget (eg, not a close box or drag handle) +// Returns true if found +static bool get_last_child_rect(LLView* parent, LLRect *rect) +{ + if (!parent) return false; + + LLView::child_list_t::const_iterator itor = + parent->getChildList()->begin(); + for (;itor != parent->getChildList()->end(); ++itor) + { + LLView *last_view = (*itor); + if (last_view->getFromXUI()) + { + *rect = last_view->getRect(); + return true; } } + return false; +} - if (node->hasAttribute("width")) - { - node->getAttributeS32("width", w); - } - if (node->hasAttribute("height")) +//static +void LLView::applyXUILayout(LLView::Params& p, LLView* parent) +{ + const S32 VPAD = 4; + const S32 MIN_WIDGET_HEIGHT = 10; + + // *NOTE: This will confuse export of floater/panel coordinates unless + // the default is also "topleft". JC + if (p.layout().empty() && parent) { - node->getAttributeS32("height", h); + p.layout = parent->getLayout(); } - if (parent_view) + + if (parent) { - if (node->hasAttribute("left_delta")) - { - S32 left_delta = 0; - node->getAttributeS32("left_delta", left_delta); - x = last_x + left_delta; - } - else if (node->hasAttribute("left") && node->hasAttribute("right")) + LLRect parent_rect = parent->getLocalRect(); + // overwrite uninitialized rect params, using context + LLRect last_rect = parent->getLocalRect(); + + bool layout_topleft = (p.layout() == "topleft"); + if (layout_topleft) { - // compute width based on left and right - S32 right = 0; - node->getAttributeS32("right", right); - if (right < 0) - { - right = parent_view->getRect().getWidth() + right; - } - w = right - x; + //invert top to bottom + if (p.rect.top.isProvided()) p.rect.top = parent_rect.getHeight() - p.rect.top; + if (p.rect.bottom.isProvided()) p.rect.bottom = parent_rect.getHeight() - p.rect.bottom; } - else if (node->hasAttribute("left")) + + // convert negative or centered coordinates to parent relative values + // Note: some of this logic matches the logic in TypedParam<LLRect>::setValueFromBlock() + + if (p.center_horiz) { - if (x < 0) + if (p.rect.left.isProvided() && p.rect.right.isProvided()) { - x = parent_view->getRect().getWidth() + x; - follows |= FOLLOWS_RIGHT; + S32 width = p.rect.right - p.rect.left; + width = llmax(width, 0); + S32 offset = parent_rect.getWidth()/2 - width/2; + p.rect.left = p.rect.left + offset; + p.rect.right = p.rect.right + offset; } else { - follows |= FOLLOWS_LEFT; + p.rect.left = p.rect.left + parent_rect.getWidth()/2 - p.rect.width/2; + p.rect.right.setProvided(false); // recalculate the right } } - else if (node->hasAttribute("width") && node->hasAttribute("right")) + else + { + if (p.rect.left.isProvided() && p.rect.left < 0) p.rect.left = p.rect.left + parent_rect.getWidth(); + if (p.rect.right.isProvided() && p.rect.right < 0) p.rect.right = p.rect.right + parent_rect.getWidth(); + } + if (p.center_vert) { - S32 right = 0; - node->getAttributeS32("right", right); - if (right < 0) + if (p.rect.bottom.isProvided() && p.rect.top.isProvided()) { - right = parent_view->getRect().getWidth() + right; + S32 height = p.rect.top - p.rect.bottom; + height = llmax(height, 0); + S32 offset = parent_rect.getHeight()/2 - height/2; + p.rect.bottom = p.rect.bottom + offset; + p.rect.top = p.rect.top + offset; + } + else + { + p.rect.bottom = p.rect.bottom + parent_rect.getHeight()/2 - p.rect.height/2; + p.rect.top.setProvided(false); // recalculate the top } - x = right - w; } else { - // left not specified, same as last - x = last_x; + if (p.rect.bottom.isProvided() && p.rect.bottom < 0) p.rect.bottom = p.rect.bottom + parent_rect.getHeight(); + if (p.rect.top.isProvided() && p.rect.top < 0) p.rect.top = p.rect.top + parent_rect.getHeight(); } - if (node->hasAttribute("bottom_delta")) + + // DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels + if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0) { - S32 bottom_delta = 0; - node->getAttributeS32("bottom_delta", bottom_delta); - y = last_y + bottom_delta; + p.rect.height = MIN_WIDGET_HEIGHT; } - else if (node->hasAttribute("top")) + + last_rect.translate(0, last_rect.getHeight()); + + // If there was a recently constructed child, use its rectangle + get_last_child_rect(parent, &last_rect); + + if (layout_topleft) { - // compute height based on top - S32 top = 0; - node->getAttributeS32("top", top); - if (top < 0) + p.bottom_delta.setIfNotProvided(0, false); + + // Invert the sense of bottom_delta for topleft layout + if (p.bottom_delta.isProvided()) { - top = parent_view->getRect().getHeight() + top; + p.bottom_delta = -p.bottom_delta; } - h = top - y; - } - else if (node->hasAttribute("bottom")) - { - if (y < 0) + else if (p.top_pad.isProvided()) { - y = parent_view->getRect().getHeight() + y; - follows |= FOLLOWS_TOP; + p.bottom_delta = -(p.rect.height + p.top_pad); } - else + else if (p.top_delta.isProvided()) { - follows |= FOLLOWS_BOTTOM; + p.bottom_delta = + -(p.top_delta + p.rect.height - last_rect.getHeight()); } - } - else - { - // if bottom not specified, generate automatically - if (last_y == 0) + else if (!p.bottom_delta.isProvided() + && !p.left_delta.isProvided() + && !p.top_pad.isProvided() + && !p.left_pad.isProvided()) { - // treat first child as "bottom" - y = parent_view->getRect().getHeight() - (h + VPAD); - follows |= FOLLOWS_TOP; + // set default position is just below last rect + p.bottom_delta.set(-(p.rect.height + VPAD), false); } - else + + // default to same left edge + p.left_delta.setIfNotProvided(0, false); + if (p.left_pad.isProvided()) { - // treat subsequent children as "bottom_delta" - y = last_y - (h + VPAD); + // left_pad is based on prior widget's right edge + p.left_delta.set(p.left_pad + last_rect.getWidth(), false); } + + last_rect.translate(p.left_delta, p.bottom_delta); + } + else + { + // set default position is just below last rect + p.bottom_delta.setIfNotProvided(-(p.rect.height + VPAD), false); + p.left_delta.setIfNotProvided(0, false); + last_rect.translate(p.left_delta, p.bottom_delta); } - } - else - { - x = llmax(x, 0); - y = llmax(y, 0); - follows = FOLLOWS_LEFT | FOLLOWS_TOP; - } - rect.setOriginAndSize(x, y, w, h); - - return follows; -} - -void LLView::initFromXML(LLXMLNodePtr node, LLView* parent) -{ - // create rect first, as this will supply initial follows flags - LLRect view_rect; - U32 follows_flags = createRect(node, view_rect, parent, getRequiredRect()); - // call reshape in case there are any child elements that need to be layed out - reshape(view_rect.getWidth(), view_rect.getHeight()); - setRect(view_rect); - setFollows(follows_flags); - - parseFollowsFlags(node); - if (node->hasAttribute("control_name")) - { - std::string control_name; - node->getAttributeString("control_name", control_name); - setControlName(control_name, NULL); - } + // this handles case where *both* x and x_delta are provided + // ignore x in favor of default x + x_delta + if (p.bottom_delta.isProvided()) p.rect.bottom.set(0, false); + if (p.left_delta.isProvided()) p.rect.left.set(0, false); - if (node->hasAttribute("tool_tip")) - { - std::string tool_tip_msg; - node->getAttributeString("tool_tip", tool_tip_msg); - setToolTip(tool_tip_msg); + // selectively apply rectangle defaults, making sure that + // params are not flagged as having been "provided" + // as rect params are overconstrained and rely on provided flags + p.rect.left.setIfNotProvided(last_rect.mLeft, false); + p.rect.bottom.setIfNotProvided(last_rect.mBottom, false); + p.rect.top.setIfNotProvided(last_rect.mTop, false); + p.rect.right.setIfNotProvided(last_rect.mRight, false); + p.rect.width.setIfNotProvided(last_rect.getWidth(), false); + p.rect.height.setIfNotProvided(last_rect.getHeight(), false); } +} - if (node->hasAttribute("enabled")) - { - BOOL enabled; - node->getAttributeBOOL("enabled", enabled); - setEnabled(enabled); - } - - if (node->hasAttribute("visible")) +static S32 invert_vertical(S32 y, LLView* parent) +{ + if (y < 0) { - BOOL visible; - node->getAttributeBOOL("visible", visible); - setVisible(visible); + // already based on top-left, just invert + return -y; } - - if (node->hasAttribute("hover_cursor")) + else if (parent) { - std::string cursor_string; - node->getAttributeString("hover_cursor", cursor_string); - mHoverCursor = getCursorFromString(cursor_string); + // use parent to flip coordinate + S32 parent_height = parent->getRect().getHeight(); + return parent_height - y; } - - node->getAttributeBOOL("use_bounding_rect", mUseBoundingRect); - node->getAttributeBOOL("mouse_opaque", mMouseOpaque); - - node->getAttributeS32("default_tab_group", mDefaultTabGroup); - - reshape(view_rect.getWidth(), view_rect.getHeight()); -} - -void LLView::parseFollowsFlags(LLXMLNodePtr node) -{ - if (node->hasAttribute("follows")) + else { - setFollowsNone(); - - std::string follows; - node->getAttributeString("follows", follows); - - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep("|"); - tokenizer tokens(follows, sep); - tokenizer::iterator token_iter = tokens.begin(); - - while(token_iter != tokens.end()) - { - const std::string& token_str = *token_iter; - if (token_str == "left") - { - setFollowsLeft(); - } - else if (token_str == "right") + llwarns << "Attempting to convert layout to top-left with no parent" << llendl; + return y; + } +} + +// Assumes that input is in bottom-left coordinates, hence must call +// _before_ convert_coords_to_top_left(). +static void convert_to_relative_layout(LLView::Params& p, LLView* parent) +{ + // Use setupParams to get the final widget rectangle + // according to our wacky layout rules. + LLView::Params final = p; + LLView::applyXUILayout(final, parent); + // Must actually extract the rectangle to get consistent + // right = left+width, top = bottom+height + LLRect final_rect = final.rect; + + // We prefer to write out top edge instead of bottom, regardless + // of whether we use relative positioning + bool converted_top = false; + + // Look for a last rectangle + LLRect last_rect; + if (get_last_child_rect(parent, &last_rect)) + { + // ...we have a previous widget to compare to + const S32 EDGE_THRESHOLD_PIXELS = 4; + S32 left_pad = final_rect.mLeft - last_rect.mRight; + S32 left_delta = final_rect.mLeft - last_rect.mLeft; + S32 top_pad = final_rect.mTop - last_rect.mBottom; + S32 top_delta = final_rect.mTop - last_rect.mTop; + // If my left edge is almost the same, or my top edge is + // almost the same... + if (llabs(left_delta) <= EDGE_THRESHOLD_PIXELS + || llabs(top_delta) <= EDGE_THRESHOLD_PIXELS) + { + // ...use relative positioning + // prefer top_pad if widgets are stacking vertically + // (coordinate system is still bottom-left here) + if (top_pad < 0) { - setFollowsRight(); + p.top_pad = top_pad; + p.top_delta.setProvided(false); } - else if (token_str == "top") + else { - setFollowsTop(); + p.top_pad.setProvided(false); + p.top_delta = top_delta; } - else if (token_str == "bottom") + // null out other vertical specifiers + p.rect.top.setProvided(false); + p.rect.bottom.setProvided(false); + p.bottom_delta.setProvided(false); + converted_top = true; + + // prefer left_pad if widgets are stacking horizontally + if (left_pad > 0) { - setFollowsBottom(); + p.left_pad = left_pad; + p.left_delta.setProvided(false); } - else if (token_str == "all") + else { - setFollowsAll(); + p.left_pad.setProvided(false); + p.left_delta = left_delta; } - ++token_iter; + p.rect.left.setProvided(false); + p.rect.right.setProvided(false); } } + + if (!converted_top) + { + // ...this is the first widget, or one that wasn't aligned + // prefer top/left specification + p.rect.top = final_rect.mTop; + p.rect.bottom.setProvided(false); + p.bottom_delta.setProvided(false); + p.top_pad.setProvided(false); + p.top_delta.setProvided(false); + } } -// static -LLFontGL* LLView::selectFont(LLXMLNodePtr node) +static void convert_coords_to_top_left(LLView::Params& p, LLView* parent) { - std::string font_name, font_size, font_style; - U8 style = 0; - - if (node->hasAttribute("font")) + // Convert the coordinate system to be top-left based. + if (p.rect.top.isProvided()) { - node->getAttributeString("font", font_name); + p.rect.top = invert_vertical(p.rect.top, parent); } - - if (node->hasAttribute("font_size")) + if (p.rect.bottom.isProvided()) { - node->getAttributeString("font_size", font_size); + p.rect.bottom = invert_vertical(p.rect.bottom, parent); } - - if (node->hasAttribute("font_style")) + if (p.top_pad.isProvided()) { - node->getAttributeString("font_style", font_style); - style = LLFontGL::getStyleFromString(font_style); + p.top_pad = -p.top_pad; } - - if (node->hasAttribute("font-style")) + if (p.top_delta.isProvided()) { - node->getAttributeString("font-style", font_style); - style = LLFontGL::getStyleFromString(font_style); + p.top_delta = -p.top_delta; } - - if (font_name.empty()) - return NULL; - - LLFontDescriptor desc(font_name, font_size, style); - LLFontGL* gl_font = LLFontGL::getFont(desc); - - return gl_font; -} - -// static -LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node) -{ - LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT; - - if (node->hasAttribute("halign")) + if (p.bottom_delta.isProvided()) { - std::string horizontal_align_name; - node->getAttributeString("halign", horizontal_align_name); - gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name); + p.bottom_delta = -p.bottom_delta; } - return gl_hfont_align; + p.layout = "topleft"; } -// static -LLFontGL::VAlign LLView::selectFontVAlign(LLXMLNodePtr node) +//static +void LLView::setupParamsForExport(Params& p, LLView* parent) { - LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE; - - if (node->hasAttribute("valign")) + // Don't convert if already top-left based + if (p.layout() == "topleft") { - std::string vert_align_name; - node->getAttributeString("valign", vert_align_name); - gl_vfont_align = LLFontGL::vAlignFromName(vert_align_name); + return; } - return gl_vfont_align; -} - -// static -LLFontGL::StyleFlags LLView::selectFontStyle(LLXMLNodePtr node) -{ - LLFontGL::StyleFlags gl_font_style = LLFontGL::NORMAL; - if (node->hasAttribute("style")) + // heuristic: Many of our floaters and panels were bulk-exported. + // These specify exactly bottom/left and height/width. + // Others were done by hand using bottom_delta and/or left_delta. + // Some rely on not specifying left to mean align with left edge. + // Try to convert both to use relative layout, but using top-left + // coordinates. + // Avoid rectangles where top/bottom/left/right was specified. + if (p.rect.height.isProvided() && p.rect.width.isProvided()) { - std::string style_flags_name; - node->getAttributeString("style", style_flags_name); - - if (style_flags_name == "normal") - { - gl_font_style = LLFontGL::NORMAL; - } - else if (style_flags_name == "bold") + if (p.rect.bottom.isProvided() && p.rect.left.isProvided()) { - gl_font_style = LLFontGL::BOLD; + // standard bulk export, convert it + convert_to_relative_layout(p, parent); } - else if (style_flags_name == "italic") + else if (p.rect.bottom.isProvided() && p.left_delta.isProvided()) { - gl_font_style = LLFontGL::ITALIC; + // hand layout with left_delta + convert_to_relative_layout(p, parent); } - else if (style_flags_name == "underline") + else if (p.bottom_delta.isProvided()) { - gl_font_style = LLFontGL::UNDERLINE; + // hand layout with bottom_delta + // don't check for p.rect.left or p.left_delta because sometimes + // this layout doesn't set it for widgets that are left-aligned + convert_to_relative_layout(p, parent); } - //else leave left } - return gl_font_style; + + convert_coords_to_top_left(p, parent); } -bool LLView::setControlValue(const LLSD& value) -{ - std::string ctrlname = getControlName(); - if (!ctrlname.empty()) - { - LLUI::sConfigGroup->setValue(ctrlname, value); - return true; - } - return false; +LLView::tree_iterator_t LLView::beginTreeDFS() +{ + return tree_iterator_t(this, + boost::bind(boost::mem_fn(&LLView::beginChild), _1), + boost::bind(boost::mem_fn(&LLView::endChild), _1)); } -//virtual -void LLView::setControlName(const std::string& control_name, LLView *context) -{ - if (context == NULL) - { - context = this; - } +LLView::tree_iterator_t LLView::endTreeDFS() +{ + // an empty iterator is an "end" iterator + return tree_iterator_t(); +} - if (!mControlName.empty()) - { - llwarns << "setControlName called twice on same control!" << llendl; - mControlConnection.disconnect(); // disconnect current signal - mControlName.clear(); - } - - // Register new listener - if (!control_name.empty()) - { - LLControlVariable *control = context->findControl(control_name); - if (control) - { - mControlName = control_name; - mControlConnection = control->getSignal()->connect(boost::bind(&controlListener, _1, getHandle(), std::string("value"))); - setValue(control->getValue()); - } - } +LLView::tree_post_iterator_t LLView::beginTreeDFSPost() +{ + return tree_post_iterator_t(this, + boost::bind(boost::mem_fn(&LLView::beginChild), _1), + boost::bind(boost::mem_fn(&LLView::endChild), _1)); } -// static -bool LLView::controlListener(const LLSD& newvalue, LLHandle<LLView> handle, std::string type) +LLView::tree_post_iterator_t LLView::endTreeDFSPost() +{ + // an empty iterator is an "end" iterator + return tree_post_iterator_t(); +} + + +LLView::root_to_view_iterator_t LLView::beginRootToView() { - LLView* view = handle.get(); - if (view) - { - if (type == "value") - { - view->setValue(newvalue); - return true; - } - else if (type == "enabled") - { - view->setEnabled(newvalue.asBoolean()); - return true; - } - else if (type == "visible") - { - view->setVisible(newvalue.asBoolean()); - return true; - } - } - return false; + return root_to_view_iterator_t(this, boost::bind(&LLView::getParent, _1)); } -void LLView::addBoolControl(const std::string& name, bool initial_value) +LLView::root_to_view_iterator_t LLView::endRootToView() { - mFloaterControls[name] = new LLControlVariable(name, TYPE_BOOLEAN, initial_value, std::string("Internal floater control")); + return root_to_view_iterator_t(); } -LLControlVariable *LLView::getControl(const std::string& name) + +// only create maps on demand, as they incur heap allocation/deallocation cost +// when a view is constructed/deconstructed +LLView::default_widget_map_t& LLView::getDefaultWidgetMap() const { - control_map_t::iterator itor = mFloaterControls.find(name); - if (itor != mFloaterControls.end()) + if (!mDefaultWidgets) { - return itor->second; + mDefaultWidgets = new default_widget_map_t(); } - return NULL; + return *mDefaultWidgets; } -//virtual -void LLView::setValue(const LLSD& value) +S32 LLView::notifyParent(const LLSD& info) { + LLView* parent = getParent(); + if(parent) + return parent->notifyParent(info); + return 0; } - -//virtual -LLSD LLView::getValue() const +bool LLView::notifyChildren(const LLSD& info) { - return LLSD(); + bool ret = false; + for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + { + ret |= (*child_it)->notifyChildren(info); + } + return ret; } -LLView* LLView::createWidget(LLXMLNodePtr xml_node) const +// convenient accessor for draw context +const LLViewDrawContext& LLView::getDrawContext() { - // forward requests to ui ctrl factory - return LLUICtrlFactory::getInstance()->createCtrlWidget(NULL, xml_node); -} - -// -// LLWidgetClassRegistry -// - -LLWidgetClassRegistry::LLWidgetClassRegistry() -{ -} - -void LLWidgetClassRegistry::registerCtrl(const std::string& tag, LLWidgetClassRegistry::factory_func_t function) -{ - std::string lower_case_tag = tag; - LLStringUtil::toLower(lower_case_tag); - - mCreatorFunctions[lower_case_tag] = function; + return LLViewDrawContext::getCurrentContext(); } -BOOL LLWidgetClassRegistry::isTagRegistered(const std::string &tag) -{ - return mCreatorFunctions.find(tag) != mCreatorFunctions.end(); -} +const LLViewDrawContext& LLViewDrawContext::getCurrentContext() +{ + static LLViewDrawContext default_context; -LLWidgetClassRegistry::factory_func_t LLWidgetClassRegistry::getCreatorFunc(const std::string& ctrl_type) -{ - factory_map_t::const_iterator found_it = mCreatorFunctions.find(ctrl_type); - if (found_it == mCreatorFunctions.end()) - { - return NULL; - } - return found_it->second; + if (sDrawContextStack.empty()) + return default_context; + + return *sDrawContextStack.back(); } - |