diff options
Diffstat (limited to 'indra/llui/llmenugl.cpp')
-rw-r--r-- | indra/llui/llmenugl.cpp | 943 |
1 files changed, 559 insertions, 384 deletions
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 4af1c1241b..6d590cf54e 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -2,31 +2,25 @@ * @file llmenugl.cpp * @brief LLMenuItemGL base class * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -46,17 +40,19 @@ #include "llmenugl.h" +#include "llgl.h" #include "llmath.h" #include "llrender.h" #include "llfocusmgr.h" -#include "llfont.h" #include "llcoord.h" #include "llwindow.h" #include "llcriticaldamp.h" #include "lluictrlfactory.h" +#include "llbutton.h" #include "llfontgl.h" #include "llresmgr.h" +#include "lltrans.h" #include "llui.h" #include "llstl.h" @@ -75,10 +71,6 @@ S32 MENU_BAR_WIDTH = 0; /// Local function declarations, constants, enums, and typedefs ///============================================================================ -const std::string SEPARATOR_NAME("separator"); -const std::string SEPARATOR_LABEL( "-----------" ); -const std::string VERTICAL_SEPARATOR_LABEL( "|" ); - const S32 LABEL_BOTTOM_PAD_PIXELS = 2; const U32 LEFT_PAD_PIXELS = 3; @@ -89,7 +81,6 @@ const U32 RIGHT_PAD_PIXELS = 2; const U32 RIGHT_WIDTH_PIXELS = 15; const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; -const U32 ACCEL_PAD_PIXELS = 10; const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS; const U32 BRIEF_PAD_PIXELS = 2; @@ -98,10 +89,14 @@ const U32 SEPARATOR_HEIGHT_PIXELS = 8; const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10; const S32 MENU_ITEM_PADDING = 4; -const std::string BOOLEAN_TRUE_PREFIX( "X" ); -const std::string BRANCH_SUFFIX( ">" ); -const std::string ARROW_UP ("^^^^^^^"); -const std::string ARROW_DOWN("vvvvvvv"); +const std::string SEPARATOR_NAME("separator"); +const std::string SEPARATOR_LABEL( "-----------" ); +const std::string VERTICAL_SEPARATOR_LABEL( "|" ); + +const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK +const std::string LLMenuGL::BRANCH_SUFFIX( "\xE2\x96\xB6" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE +const std::string LLMenuGL::ARROW_UP ("^^^^^^^"); +const std::string LLMenuGL::ARROW_DOWN("vvvvvvv"); const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f; @@ -119,27 +114,49 @@ const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bo const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f; -// widget registrars -struct MenuRegistry : public LLWidgetRegistry<MenuRegistry> -{}; - +static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item"); static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator"); static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call"); static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check"); +// Created programmatically but we need to specify custom colors in xml +static MenuRegistry::Register<LLMenuItemTearOffGL> register_menu_item_tear_off("menu_item_tear_off"); static MenuRegistry::Register<LLMenuGL> register_menu("menu"); -static LLDefaultWidgetRegistry::Register<LLMenuGL> register_menu_default("menu"); +static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu"); ///============================================================================ /// Class LLMenuItemGL ///============================================================================ + +LLMenuItemGL::Params::Params() +: shortcut("shortcut"), + jump_key("jump_key", KEY_NONE), + use_mac_ctrl("use_mac_ctrl", false), + allow_key_repeat("allow_key_repeat", false), + rect("rect"), + left("left"), + top("top"), + right("right"), + bottom("bottom"), + width("width"), + height("height"), + bottom_delta("bottom_delta"), + left_delta("left_delta"), + enabled_color("enabled_color"), + disabled_color("disabled_color"), + highlight_bg_color("highlight_bg_color"), + highlight_fg_color("highlight_fg_color") +{ + mouse_opaque = true; +} + // Default constructor LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) : LLUICtrl(p), mJumpKey(p.jump_key), - mAllowKeyRepeat(FALSE), + mAllowKeyRepeat(p.allow_key_repeat), mHighlight( FALSE ), mGotHover( FALSE ), mBriefItem( FALSE ), @@ -181,8 +198,27 @@ LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) std::string key_str = shortcut.substr(pipe_pos+1); LLKeyboard::keyFromString(key_str, &mAcceleratorKey); + + LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut + << ", key str: " << key_str + << ", accelerator mask: " << mAcceleratorMask + << ", accelerator key: " << mAcceleratorKey + << LL_ENDL; } +//virtual +void LLMenuItemGL::setValue(const LLSD& value) +{ + setLabel(value.asString()); +} + +//virtual +LLSD LLMenuItemGL::getValue() const +{ + return getLabel(); +} + +//virtual BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) { if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) @@ -200,6 +236,26 @@ BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) return TRUE; } +//virtual +BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + return LLUICtrl::handleRightMouseDown(x,y,mask); +} + +//virtual +BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + // If this event came from a right-click context menu spawn, + // process as a left-click to allow menu items to be hit + if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX + || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX) + { + BOOL handled = handleMouseUp(x, y, mask); + return handled; + } + return LLUICtrl::handleRightMouseUp(x,y,mask); +} + // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp) @@ -249,47 +305,8 @@ BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp) // the current accelerator key and mask to the provided string. void LLMenuItemGL::appendAcceleratorString( std::string& st ) const { - // break early if this is a silly thing to do. - if( KEY_NONE == mAcceleratorKey ) - { - return; - } - - // Append any masks -#ifdef LL_DARWIN - // Standard Mac names for modifier keys in menu equivalents - // We could use the symbol characters, but they only exist in certain fonts. - if( mAcceleratorMask & MASK_CONTROL ) - { - if ( mAcceleratorMask & MASK_MAC_CONTROL ) - { - st.append( "Ctrl-" ); - } - else - { - st.append( "Cmd-" ); // Symbol would be "\xE2\x8C\x98" - } - } - if( mAcceleratorMask & MASK_ALT ) - st.append( "Opt-" ); // Symbol would be "\xE2\x8C\xA5" - if( mAcceleratorMask & MASK_SHIFT ) - st.append( "Shift-" ); // Symbol would be "\xE2\x8C\xA7" -#else - if( mAcceleratorMask & MASK_CONTROL ) - st.append( "Ctrl-" ); - if( mAcceleratorMask & MASK_ALT ) - st.append( "Alt-" ); - if( mAcceleratorMask & MASK_SHIFT ) - st.append( "Shift-" ); -#endif - - std::string keystr = LLKeyboard::stringFromKey( mAcceleratorKey ); - if ((mAcceleratorMask & MASK_NORMALKEYS) && - (keystr[0] == '-' || keystr[0] == '=')) - { - st.append( " " ); - } - st.append( keystr ); + st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey ); + LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL; } void LLMenuItemGL::setJumpKey(KEY key) @@ -304,9 +321,20 @@ U32 LLMenuItemGL::getNominalHeight( void ) const return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; } +//virtual +void LLMenuItemGL::setBriefItem(BOOL brief) +{ + mBriefItem = brief; +} + +//virtual +BOOL LLMenuItemGL::isBriefItem() const +{ + return mBriefItem; +} // Get the parent menu for this item -LLMenuGL* LLMenuItemGL::getMenu() +LLMenuGL* LLMenuItemGL::getMenu() const { return (LLMenuGL*) getParent(); } @@ -330,7 +358,7 @@ U32 LLMenuItemGL::getNominalWidth( void ) const if( KEY_NONE != mAcceleratorKey ) { - width += ACCEL_PAD_PIXELS; + width += getMenu()->getShortcutPad(); std::string temp; appendAcceleratorString( temp ); width += mFont->getWidth( temp ); @@ -350,8 +378,10 @@ void LLMenuItemGL::buildDrawLabel( void ) void LLMenuItemGL::onCommit( void ) { - // close all open menus by default - // if parent menu is actually visible (and we are not triggering menu item via accelerator) + // Check torn-off status to allow left-arrow keyboard navigation back + // to parent menu. + // Also, don't hide if item triggered by keyboard shortcut (and hence + // parent not visible). if (!getMenu()->getTornOff() && getMenu()->getVisible()) { @@ -368,6 +398,12 @@ void LLMenuItemGL::onCommit( void ) { getMenu()->clearHoverItem(); } + + if (mHighlight != highlight) + { + dirtyRect(); + } + mHighlight = highlight; } @@ -444,17 +480,12 @@ void LLMenuItemGL::draw( void ) if (dynamic_cast<LLMenuItemCallGL*>(this)) debug_count++; gGL.color4fv( mHighlightBackground.get().mV ); + gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); } LLColor4 color; - LLFontGL::ShadowType shadow_style = LLFontGL::NO_SHADOW; - if (getEnabled() && !mDrawTextDisabled ) - { - shadow_style = LLFontGL::DROP_SHADOW_SOFT; - } - if ( getEnabled() && getHighlight() ) { color = mHighlightForeground.get(); @@ -472,26 +503,26 @@ void LLMenuItemGL::draw( void ) if (mBriefItem) { mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style ); + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL); } else { if( !mDrawBoolLabel.empty() ) { mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style, S32_MAX, S32_MAX, NULL, FALSE ); + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style, S32_MAX, S32_MAX, NULL, FALSE ); + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); if( !mDrawAccelLabel.empty() ) { mFont->render( mDrawAccelLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, - LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style, S32_MAX, S32_MAX, NULL, FALSE ); + LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } if( !mDrawBranchLabel.empty() ) { mFont->render( mDrawBranchLabel.getWString(), 0, (F32)getRect().mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color, - LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style, S32_MAX, S32_MAX, NULL, FALSE ); + LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); } } @@ -519,12 +550,13 @@ BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& return TRUE; } -void LLMenuItemGL::onVisibilityChange(BOOL new_visibility) +void LLMenuItemGL::handleVisibilityChange(BOOL new_visibility) { if (getMenu()) { getMenu()->needsArrange(); } + LLView::handleVisibilityChange(new_visibility); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -562,12 +594,13 @@ BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) LLMenuGL* parent_menu = getMenu(); if (y > getRect().getHeight() / 2) { - LLView* prev_menu_item = parent_menu->findPrevSibling(this); + // the menu items are in the child list in bottom up order + LLView* prev_menu_item = parent_menu->findNextSibling(this); return prev_menu_item ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; } else { - LLView* next_menu_item = parent_menu->findNextSibling(this); + LLView* next_menu_item = parent_menu->findPrevSibling(this); return next_menu_item ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE; } } @@ -577,12 +610,12 @@ BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) LLMenuGL* parent_menu = getMenu(); if (y > getRect().getHeight() / 2) { - LLView* prev_menu_item = parent_menu->findPrevSibling(this); + LLView* prev_menu_item = parent_menu->findNextSibling(this); return prev_menu_item ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; } else { - LLView* next_menu_item = parent_menu->findNextSibling(this); + LLView* next_menu_item = parent_menu->findPrevSibling(this); return next_menu_item ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE; } } @@ -626,11 +659,38 @@ LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void ) // Class LLMenuItemTearOffGL //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p) -: LLMenuItemGL(p), - mParentHandle(p.parent_floater_handle) +: LLMenuItemGL(p) { } +// Returns the first floater ancestor if there is one +LLFloater* LLMenuItemTearOffGL::getParentFloater() +{ + LLView* parent_view = getMenu(); + + while (parent_view) + { + if (dynamic_cast<LLFloater*>(parent_view)) + { + return dynamic_cast<LLFloater*>(parent_view); + } + + bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view); + + if (parent_is_menu) + { + // use menu parent + parent_view = dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem(); + } + else + { + // just use regular view parent + parent_view = parent_view->getParent(); + } + } + + return NULL; +} void LLMenuItemTearOffGL::onCommit() { @@ -649,7 +709,7 @@ void LLMenuItemTearOffGL::onCommit() getMenu()->needsArrange(); - LLFloater* parent_floater = mParentHandle.get(); + LLFloater* parent_floater = getParentFloater(); LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu()); if (tear_off_menu) @@ -730,19 +790,27 @@ LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p) void LLMenuItemCallGL::initFromParams(const Params& p) { + if (p.on_visible.isProvided()) + { + mVisibleSignal.connect(initEnableCallback(p.on_visible)); + } if (p.on_enable.isProvided()) { - initEnableCallback(p.on_enable, mEnableSignal); + setEnableCallback(initEnableCallback(p.on_enable)); // Set the enabled control variable (for backwards compatability) if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty()) { LLControlVariable* control = findControl(p.on_enable.control_name()); if (control) + { setEnabledControlVariable(control); + } } } if (p.on_click.isProvided()) - initCommitCallback(p.on_click, mCommitSignal); + { + setCommitCallback(initCommitCallback(p.on_click)); + } LLUICtrl::initFromParams(p); } @@ -763,7 +831,10 @@ void LLMenuItemCallGL::updateEnabled( void ) if (mEnabledControlVariable) { if (!enabled) - mEnabledControlVariable->set(false); // callback overrides control variable; this will call setEnabled() + { + // callback overrides control variable; this will call setEnabled() + mEnabledControlVariable->set(false); + } } else { @@ -772,9 +843,19 @@ void LLMenuItemCallGL::updateEnabled( void ) } } +void LLMenuItemCallGL::updateVisible( void ) +{ + if (mVisibleSignal.num_slots() > 0) + { + bool visible = mVisibleSignal(this, LLSD()); + setVisible(visible); + } +} + void LLMenuItemCallGL::buildDrawLabel( void ) { updateEnabled(); + updateVisible(); LLMenuItemGL::buildDrawLabel(); } @@ -797,6 +878,9 @@ BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask ) return FALSE; } +// handleRightMouseUp moved into base class LLMenuItemGL so clicks are +// handled for all menu item types + ///============================================================================ /// Class LLMenuItemCheckGL ///============================================================================ @@ -809,7 +893,7 @@ void LLMenuItemCheckGL::initFromParams(const Params& p) { if (p.on_check.isProvided()) { - initEnableCallback(p.on_check, mCheckSignal); + setCheckCallback(initEnableCallback(p.on_check)); // Set the control name (for backwards compatability) if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty()) { @@ -831,7 +915,7 @@ void LLMenuItemCheckGL::setValue(const LLSD& value) LLUICtrl::setValue(value); if(value.asBoolean()) { - mDrawBoolLabel = BOOLEAN_TRUE_PREFIX; + mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; } else { @@ -839,6 +923,15 @@ void LLMenuItemCheckGL::setValue(const LLSD& value) } } +//virtual +LLSD LLMenuItemCheckGL::getValue() const +{ + // Get our boolean value from the view model. + // If we don't override this method then the implementation from + // LLMenuItemGL will return a string. (EXT-8501) + return LLUICtrl::getValue(); +} + // called to rebuild the draw label void LLMenuItemCheckGL::buildDrawLabel( void ) { @@ -855,7 +948,7 @@ void LLMenuItemCheckGL::buildDrawLabel( void ) } if(getValue().asBoolean()) { - mDrawBoolLabel = BOOLEAN_TRUE_PREFIX; + mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX; } else { @@ -885,24 +978,38 @@ LLMenuItemBranchGL::~LLMenuItemBranchGL() } // virtual -LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const +LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) const { LLMenuGL* branch = getBranch(); - if (!branch) - return LLView::getChildView(name, recurse, create_if_missing); - - // richard: this is redundant with parent, remove - if (branch->getName() == name) + if (branch) { - return branch; + if (branch->getName() == name) + { + return branch; + } + + // Always recurse on branches + return branch->getChildView(name, recurse); } - // Always recurse on branches - LLView* child = branch->getChildView(name, recurse, FALSE); - if (!child) + + return LLView::getChildView(name, recurse); +} + +LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) const +{ + LLMenuGL* branch = getBranch(); + if (branch) { - child = LLView::getChildView(name, recurse, create_if_missing); + if (branch->getName() == name) + { + return branch; + } + + // Always recurse on branches + return branch->findChildView(name, recurse); } - return child; + + return LLView::findChildView(name, recurse); } // virtual @@ -951,7 +1058,7 @@ void LLMenuItemBranchGL::buildDrawLabel( void ) std::string st = mDrawAccelLabel; appendAcceleratorString( st ); mDrawAccelLabel = st; - mDrawBranchLabel = BRANCH_SUFFIX; + mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; } void LLMenuItemBranchGL::onCommit( void ) @@ -1061,13 +1168,13 @@ void LLMenuItemBranchGL::updateBranchParent(LLView* parentp) } } -void LLMenuItemBranchGL::onVisibilityChange( BOOL new_visibility ) +void LLMenuItemBranchGL::handleVisibilityChange( BOOL new_visibility ) { if (new_visibility == FALSE && getBranch() && !getBranch()->getTornOff()) { getBranch()->setVisible(FALSE); } - LLMenuItemGL::onVisibilityChange(new_visibility); + LLMenuItemGL::handleVisibilityChange(new_visibility); } BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) @@ -1076,40 +1183,56 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) if (!branch) return LLMenuItemGL::handleKeyHere(key, mask); - if (getMenu()->getVisible() && branch->getVisible() && key == KEY_LEFT) + // an item is highlighted, my menu is open, and I have an active sub menu or we are in + // keyboard navigation mode + if (getHighlight() + && getMenu()->isOpen() + && (isActive() || LLMenuGL::getKeyboardMode())) { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); - - BOOL handled = branch->clearHoverItem(); - if (branch->getTornOff()) - { - ((LLFloater*)branch->getParent())->setFocus(FALSE); - } - if (handled && getMenu()->getTornOff()) + if (branch->getVisible() && key == KEY_LEFT) { - ((LLFloater*)getMenu()->getParent())->setFocus(TRUE); - } - return handled; - } + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(TRUE); - if (getHighlight() && - getMenu()->isOpen() && - key == KEY_RIGHT && !branch->getHighlightedItem()) - { - // switch to keyboard navigation mode - LLMenuGL::setKeyboardMode(TRUE); + BOOL handled = branch->clearHoverItem(); + if (branch->getTornOff()) + { + ((LLFloater*)branch->getParent())->setFocus(FALSE); + } + if (handled && getMenu()->getTornOff()) + { + ((LLFloater*)getMenu()->getParent())->setFocus(TRUE); + } + return handled; + } - LLMenuItemGL* itemp = branch->highlightNextItem(NULL); - if (itemp) + if (key == KEY_RIGHT && !branch->getHighlightedItem()) { - return TRUE; + // switch to keyboard navigation mode + LLMenuGL::setKeyboardMode(TRUE); + + LLMenuItemGL* itemp = branch->highlightNextItem(NULL); + if (itemp) + { + return TRUE; + } } } - return LLMenuItemGL::handleKeyHere(key, mask); } +//virtual +BOOL LLMenuItemBranchGL::isActive() const +{ + return isOpen() && getBranch() && getBranch()->getHighlightedItem(); +} + +//virtual +BOOL LLMenuItemBranchGL::isOpen() const +{ + return getBranch() && getBranch()->isOpen(); +} + void LLMenuItemBranchGL::openMenu() { LLMenuGL* branch = getBranch(); @@ -1129,40 +1252,45 @@ void LLMenuItemBranchGL::openMenu() branch->arrange(); - LLRect rect = branch->getRect(); + LLRect branch_rect = branch->getRect(); // calculate root-view relative position for branch menu S32 left = getRect().mRight; S32 top = getRect().mTop - getRect().mBottom; localPointToOtherView(left, top, &left, &top, branch->getParent()); - rect.setLeftTopAndSize( left, top, - rect.getWidth(), rect.getHeight() ); + branch_rect.setLeftTopAndSize( left, top, + branch_rect.getWidth(), branch_rect.getHeight() ); if (branch->getCanTearOff()) { - rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); + branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); } - branch->setRect( rect ); - S32 x = 0; - S32 y = 0; - branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); + branch->setRect( branch_rect ); + + // if branch extends outside of menu region change the direction it opens in + S32 x, y; S32 delta_x = 0; S32 delta_y = 0; + branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); if( y < menu_region_rect.mBottom ) { - delta_y = menu_region_rect.mBottom - y; + // open upwards if menu extends past bottom + // adjust by the height of the menu item branch since it is a submenu + delta_y = branch_rect.getHeight() - getRect().getHeight(); } - S32 menu_region_width = menu_region_rect.getWidth(); - if( x - menu_region_rect.mLeft > menu_region_width - rect.getWidth() ) + if( x + branch_rect.getWidth() > menu_region_rect.mRight ) { // move sub-menu over to left side - delta_x = llmax(-x, (-1 * (rect.getWidth() + getRect().getWidth()))); + delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth()))); } branch->translate( delta_x, delta_y ); + branch->setVisible( TRUE ); branch->getParent()->sendChildToFront(branch); + + dirtyRect(); } } @@ -1283,20 +1411,26 @@ void LLMenuItemBranchDownGL::openMenu( void ) // set the hover status (called by it's menu) void LLMenuItemBranchDownGL::setHighlight( BOOL highlight ) { - if (highlight == getHighlight()) return; + if (highlight == getHighlight()) + return; //NOTE: Purposely calling all the way to the base to bypass auto-open. LLMenuItemGL::setHighlight(highlight); + + LLMenuGL* branch = getBranch(); + if (!branch) + return; + if( !highlight) { - if (getBranch()->getTornOff()) + if (branch->getTornOff()) { - ((LLFloater*)getBranch()->getParent())->setFocus(FALSE); - getBranch()->clearHoverItem(); + ((LLFloater*)branch->getParent())->setFocus(FALSE); + branch->clearHoverItem(); } else { - getBranch()->setVisible( FALSE ); + branch->setVisible( FALSE ); } } } @@ -1341,7 +1475,7 @@ BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask) { BOOL menu_open = getBranch()->getVisible(); // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded - if (getHighlight() && getMenu()->getVisible() && (isActive() || LLMenuGL::getKeyboardMode())) + if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode())) { if (key == KEY_LEFT) { @@ -1414,12 +1548,6 @@ void LLMenuItemBranchDownGL::draw( void ) gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); } - LLFontGL::ShadowType shadow_style = LLFontGL::NO_SHADOW; - if (getEnabled() && !getDrawTextDisabled() ) - { - shadow_style = LLFontGL::DROP_SHADOW_SOFT; - } - LLColor4 color; if (getHighlight()) { @@ -1434,7 +1562,7 @@ void LLMenuItemBranchDownGL::draw( void ) color = mDisabledColor.get(); } getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color, - LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL, shadow_style ); + LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL); // underline navigation key only when keyboard navigation has been initiated @@ -1457,6 +1585,7 @@ void LLMenuItemBranchDownGL::draw( void ) setHover(FALSE); } + class LLMenuScrollItem : public LLMenuItemCallGL { public: @@ -1465,10 +1594,18 @@ public: ARROW_DOWN, ARROW_UP }; + struct ArrowTypes : public LLInitParam::TypeValuesHelper<EArrowType, ArrowTypes> + { + static void declareValues() + { + declare("up", ARROW_UP); + declare("down", ARROW_DOWN); + } + }; struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params> { - Optional<EArrowType> arrow_type; + Optional<EArrowType, ArrowTypes> arrow_type; Optional<CommitCallbackParam> scroll_callback; }; @@ -1500,8 +1637,6 @@ LLMenuScrollItem::LLMenuScrollItem(const Params& p) } LLButton::Params bparams; - bparams.label(""); - bparams.label_selected(""); bparams.mouse_opaque(true); bparams.scale_image(false); bparams.click_callback(p.scroll_callback); @@ -1554,8 +1689,11 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) mBackgroundColor( p.bg_color() ), mBgVisible( p.bg_visible ), mDropShadowed( p.drop_shadow ), + mHasSelection(false), mHorizontalLayout( p.horizontal_layout ), mScrollable(mHorizontalLayout ? FALSE : p.scrollable), // Scrolling is supported only for vertical layout + mMaxScrollableItems(p.max_scrollable_items), + mPreferredWidth(p.preferred_width), mKeepFixedSize( p.keep_fixed_size ), mLabel (p.label), mLastMouseX(0), @@ -1571,8 +1709,8 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) mSpilloverMenu(NULL), mJumpKey(p.jump_key), mCreateJumpKeys(p.create_jump_keys), - mParentFloaterHandle(p.parent_floater), - mNeedsArrange(FALSE) + mNeedsArrange(FALSE), + mShortcutPad(p.shortcut_pad) { typedef boost::tokenizer<boost::char_separator<char> > tokenizer; boost::char_separator<char> sep("_"); @@ -1598,7 +1736,7 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) void LLMenuGL::initFromParams(const LLMenuGL::Params& p) { LLUICtrl::initFromParams(p); - setCanTearOff(p.can_tear_off, p.parent_floater); + setCanTearOff(p.can_tear_off); } // Destroys the object @@ -1610,12 +1748,11 @@ LLMenuGL::~LLMenuGL( void ) mJumpKeys.clear(); } -void LLMenuGL::setCanTearOff(BOOL tear_off, LLHandle<LLFloater> parent_floater_handle ) +void LLMenuGL::setCanTearOff(BOOL tear_off) { if (tear_off && mTearOffItem == NULL) { LLMenuItemTearOffGL::Params p; - p.parent_floater_handle = parent_floater_handle; mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p); addChildInBack(mTearOffItem); } @@ -1646,15 +1783,17 @@ bool LLMenuGL::addChild(LLView* view, S32 tab_group) void LLMenuGL::removeChild( LLView* ctrl) { - LLMenuItemGL* itemp = dynamic_cast<LLMenuItemGL*>(ctrl); - if (itemp) + // previously a dynamic_cast with if statement to check validity + // unfortunately removeChild is called by ~LLView, and at that point the + // object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail + LLMenuItemGL* itemp = static_cast<LLMenuItemGL*>(ctrl); + + item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp)); + if (found_it != mItems.end()) { - item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp)); - if (found_it != mItems.end()) - { - mItems.erase(found_it); - } + mItems.erase(found_it); } + return LLUICtrl::removeChild(ctrl); } @@ -1664,12 +1803,6 @@ BOOL LLMenuGL::postBuild() return LLUICtrl::postBuild(); } -const widget_registry_t& LLMenuGL::getChildRegistry() const -{ - return MenuRegistry::instance(); -} - - // are we the childmost active menu and hence our jump keys should be enabled? // or are we a free-standing torn-off menu (which uses jump keys too) BOOL LLMenuGL::jumpKeysActive() @@ -1779,17 +1912,21 @@ void LLMenuGL::scrollItemsDown() item_list_t::iterator next_item_iter; - for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) + if (cur_item_iter != mItems.end()) { - if( (*next_item_iter)->getVisible()) + for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++) { - break; + if( (*next_item_iter)->getVisible()) + { + break; + } + } + + if (next_item_iter != mItems.end() && + (*next_item_iter)->getVisible()) + { + mFirstVisibleItem = *next_item_iter; } - } - - if ((*next_item_iter)->getVisible()) - { - mFirstVisibleItem = *next_item_iter; } mNeedsArrange = TRUE; @@ -1832,12 +1969,16 @@ void LLMenuGL::arrange( void ) item_list_t::iterator first_hidden_item_iter = mItems.end(); S32 height_before_first_visible_item = -1; S32 visible_items_height = 0; + U32 scrollable_items_cnt = 0; if (mHorizontalLayout) { item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) { + // do first so LLMenuGLItemCall can call on_visible to determine if visible + (*item_iter)->buildDrawLabel(); + if ((*item_iter)->getVisible()) { if (!getTornOff() @@ -1879,6 +2020,9 @@ void LLMenuGL::arrange( void ) for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) { + // do first so LLMenuGLItemCall can call on_visible to determine if visible + (*item_iter)->buildDrawLabel(); + if ((*item_iter)->getVisible()) { if (!getTornOff() @@ -1926,31 +2070,38 @@ void LLMenuGL::arrange( void ) { height_before_first_visible_item = height - (*item_iter)->getNominalHeight(); first_visible_item_iter = item_iter; + scrollable_items_cnt = 0; } - if (-1 != height_before_first_visible_item && 0 == visible_items_height && height - height_before_first_visible_item > max_height - spillover_item_height * 2) + if (-1 != height_before_first_visible_item && 0 == visible_items_height && + (++scrollable_items_cnt > mMaxScrollableItems || + height - height_before_first_visible_item > max_height - spillover_item_height * 2 )) { first_hidden_item_iter = item_iter; visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight(); + scrollable_items_cnt--; } } } } + if (mPreferredWidth < U32_MAX) + width = llmin(mPreferredWidth, max_width); + if (mScrollable) { S32 max_items_height = max_height - spillover_item_height * 2; + if (visible_items_height == 0) + visible_items_height = height - height_before_first_visible_item; + // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit - if (visible_items_height < max_items_height) + if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems) { - if (visible_items_height == 0) - { - visible_items_height = height - height_before_first_visible_item; - } - item_list_t::iterator tmp_iter(first_visible_item_iter); - while (visible_items_height < max_items_height && first_visible_item_iter != mItems.begin()) + while (visible_items_height < max_items_height && + scrollable_items_cnt < mMaxScrollableItems && + first_visible_item_iter != mItems.begin()) { if ((*first_visible_item_iter)->getVisible()) { @@ -1964,6 +2115,7 @@ void LLMenuGL::arrange( void ) { visible_items_height += (*first_visible_item_iter)->getNominalHeight(); height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight(); + scrollable_items_cnt++; } } @@ -1972,6 +2124,7 @@ void LLMenuGL::arrange( void ) { visible_items_height -= (*first_visible_item_iter)->getNominalHeight(); height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight(); + scrollable_items_cnt--; first_visible_item_iter = tmp_iter; } if (!(*first_visible_item_iter)->getVisible()) @@ -2056,7 +2209,7 @@ void LLMenuGL::arrange( void ) item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { + { if ((*item_iter)->getVisible()) { if (mScrollable) @@ -2087,7 +2240,6 @@ void LLMenuGL::arrange( void ) } } (*item_iter)->setRect( rect ); - (*item_iter)->buildDrawLabel(); } } } @@ -2115,9 +2267,9 @@ void LLMenuGL::createSpilloverBranch() // technically, you can't tear off spillover menus, but we're passing the handle // along just to be safe LLMenuGL::Params p; + std::string label = LLTrans::getString("More"); p.name("More"); - p.label("More"); // *TODO: Translate - p.parent_floater(mParentFloaterHandle); + p.label(label); p.bg_color(mBackgroundColor); p.bg_visible(true); p.can_tear_off(false); @@ -2126,7 +2278,7 @@ void LLMenuGL::createSpilloverBranch() LLMenuItemBranchGL::Params branch_params; branch_params.name = "More"; - branch_params.label = "More"; // *TODO: Translate + branch_params.label = label; branch_params.branch = mSpilloverMenu; branch_params.font.style = "italic"; @@ -2240,8 +2392,8 @@ void LLMenuGL::createJumpKeys() { char jump_key = uppercase_word[i]; - if (LLStringOps::isDigit(jump_key) || LLStringOps::isUpper(jump_key) && - mJumpKeys.find(jump_key) == mJumpKeys.end()) + if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) && + mJumpKeys.find(jump_key) == mJumpKeys.end())) { mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it))); (*item_it)->setJumpKey(jump_key); @@ -2331,10 +2483,10 @@ BOOL LLMenuGL::appendMenu( LLMenuGL* menu ) p.label = menu->getLabel(); p.branch = menu; p.jump_key = menu->getJumpKey(); - p.enabled_color=LLUI::getCachedColorFunctor("MenuItemEnabledColor"); - p.disabled_color=LLUI::getCachedColorFunctor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUI::getCachedColorFunctor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUI::getCachedColorFunctor("MenuItemHighlightFgColor"); + p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); + p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); + p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); + p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); LLMenuItemBranchGL* branch = LLUICtrlFactory::create<LLMenuItemBranchGL>(p); success &= append( branch ); @@ -2698,7 +2850,7 @@ BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask ) ((LLMenuItemGL*)viewp)->setHighlight(TRUE); LLMenuGL::setKeyboardMode(FALSE); } - mHasSelection = TRUE; + mHasSelection = true; } } } @@ -2731,6 +2883,7 @@ BOOL LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks ) return TRUE; } + void LLMenuGL::draw( void ) { if (mNeedsArrange) @@ -2741,7 +2894,7 @@ void LLMenuGL::draw( void ) if (mDropShadowed && !mTornOff) { static LLUICachedControl<S32> drop_shadow_floater ("DropShadowFloater", 0); - static LLUICachedControl<LLColor4> color_drop_shadow ("ColorDropShadow", *(new LLColor4)); + static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow"); gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, color_drop_shadow, drop_shadow_floater ); } @@ -2776,7 +2929,7 @@ void LLMenuGL::setVisible(BOOL visible) } else { - mHasSelection = FALSE; + mHasSelection = true; mFadeTimer.stop(); } @@ -2784,9 +2937,9 @@ void LLMenuGL::setVisible(BOOL visible) } } -LLMenuGL* LLMenuGL::getChildMenuByName(const std::string& name, BOOL recurse) const +LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, BOOL recurse) const { - LLView* view = getChildView(name, recurse, FALSE); + LLView* view = findChildView(name, recurse); if (view) { LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view); @@ -2825,65 +2978,66 @@ void hide_top_view( LLView* view ) } +// x and y are the desired location for the popup, in the spawning_view's +// coordinate frame, NOT necessarily the mouse location // static void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) { - const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); + const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size + const S32 CURSOR_WIDTH = 12; + + // Save click point for detecting cursor moves before mouse-up. + // Must be in local coords to compare with mouseUp events. + // If the mouse doesn't move, the menu will stay open ala the Mac. + // See also LLContextMenu::show() + S32 mouse_x, mouse_y; + + // Resetting scrolling position + if (menu->isScrollable()) + { + menu->mFirstVisibleItem = NULL; + } + + menu->setVisible( TRUE ); + + // Fix menu rect if needed. + menu->needsArrange(); + menu->arrangeAndClear(); + + LLUI::getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y); + LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y); + + const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect(); const S32 HPAD = 2; LLRect rect = menu->getRect(); - //LLView* cur_view = spawning_view; S32 left = x + HPAD; S32 top = y; spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent()); rect.setLeftTopAndSize( left, top, rect.getWidth(), rect.getHeight() ); + menu->setRect( rect ); - //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight()); - menu->setRect( rect ); + // Adjust context menu to fit onscreen + LLRect mouse_rect; + const S32 MOUSE_CURSOR_PADDING = 5; + mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING, + mouse_y + MOUSE_CURSOR_PADDING, + CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2, + CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2); + menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect, FALSE ); + menu->getParent()->sendChildToFront(menu); - // Resetting scrolling position - if (menu->isScrollable()) - { - menu->mFirstVisibleItem = NULL; - menu->needsArrange(); - } - menu->arrangeAndClear(); // Fix menu rect if needed. - rect = menu->getRect(); - S32 bottom; - left = rect.mLeft; - bottom = rect.mBottom; - //menu->getParent()->localPointToScreen( rect.mLeft, rect.mBottom, - // &left, &bottom ); - S32 delta_x = 0; - S32 delta_y = 0; - if( bottom < menu_region_rect.mBottom ) - { - // At this point, we need to move the context menu to the - // other side of the mouse. - //delta_y = menu_region_rect.mBottom - bottom; - delta_y = (rect.getHeight() + 2 * HPAD); - } - if( left > menu_region_rect.mRight - rect.getWidth() ) - { - // At this point, we need to move the context menu to the - // other side of the mouse. - //delta_x = (window_width - rect.getWidth()) - x; - delta_x = -(rect.getWidth() + 2 * HPAD); - } - menu->translate( delta_x, delta_y ); - menu->setVisible( TRUE ); - menu->getParent()->sendChildToFront(menu); } ///============================================================================ /// Class LLMenuBarGL ///============================================================================ -static LLDefaultWidgetRegistry::Register<LLMenuBarGL> r2("menu_bar"); +static LLDefaultChildRegistry::Register<LLMenuBarGL> r2("menu_bar"); LLMenuBarGL::LLMenuBarGL( const Params& p ) : LLMenuGL(p), @@ -2984,19 +3138,6 @@ BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) return LLMenuGL::handleMouseDown(x, y, mask); } -BOOL LLMenuBarGL::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - // clicks on menu bar closes existing menus from other contexts but leave - // own menu open so that we get toggle behavior - if (!getHighlightedItem() || !getHighlightedItem()->isActive()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - return LLMenuGL::handleMouseDown(x, y, mask); -} - - void LLMenuBarGL::draw() { LLMenuItemGL* itemp = getHighlightedItem(); @@ -3115,10 +3256,10 @@ BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu ) p.label = menu->getLabel(); p.visible = menu->getVisible(); p.branch = menu; - p.enabled_color=LLUI::getCachedColorFunctor("MenuItemEnabledColor"); - p.disabled_color=LLUI::getCachedColorFunctor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUI::getCachedColorFunctor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUI::getCachedColorFunctor("MenuItemHighlightFgColor"); + p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); + p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); + p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); + p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create<LLMenuItemBranchDownGL>(p); success &= branch->addToAcceleratorList(&mAccelerators); @@ -3202,11 +3343,11 @@ BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask ) ///============================================================================ /// Class LLMenuHolderGL ///============================================================================ -LLMenuHolderGL::LLMenuHolderGL() - : LLPanel() +LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX); + +LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p) + : LLPanel(p) { - setName("Menu Holder"); - setMouseOpaque(FALSE); sItemActivationTimer.stop(); mCanHide = TRUE; } @@ -3216,7 +3357,7 @@ void LLMenuHolderGL::draw() LLView::draw(); // now draw last selected item as overlay LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get(); - if (selecteditem && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME) + if (selecteditem && selecteditem->getVisible() && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME) { // make sure toggle items, for example, show the proper state when fading out selecteditem->buildDrawLabel(); @@ -3258,6 +3399,66 @@ BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask ) return handled; } +// This occurs when you mouse-down to spawn a context menu, hold the button +// down, move off the menu, then mouse-up. We want this to close the menu. +BOOL LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask ) +{ + const S32 SLOP = 2; + S32 spawn_dx = (x - sContextMenuSpawnPos.mX); + S32 spawn_dy = (y - sContextMenuSpawnPos.mY); + if (-SLOP <= spawn_dx && spawn_dx <= SLOP + && -SLOP <= spawn_dy && spawn_dy <= SLOP) + { + // we're still inside the slop region from spawning this menu + // so interpret the mouse-up as a single-click to show and leave on + // screen + sContextMenuSpawnPos.set(S32_MAX, S32_MAX); + return TRUE; + } + + BOOL handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL; + if (!handled) + { + // clicked off of menu, hide them all + hideMenus(); + } + return handled; +} + +BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ + BOOL handled = false; + LLMenuGL* const pMenu = dynamic_cast<LLMenuGL*>(getVisibleMenu()); + + if (pMenu) + { + //eat TAB key - EXT-7000 + if (key == KEY_TAB && mask == MASK_NONE) + { + return TRUE; + } + + //handle ESCAPE and RETURN key + handled = LLPanel::handleKey(key, mask, called_from_parent); + if (!handled) + { + if (pMenu->getHighlightedItem()) + { + handled = pMenu->handleKey(key, mask, TRUE); + } + else + { + //highlight first enabled one + pMenu->highlightNextItem(NULL); + handled = true; + } + } + } + + return handled; + +} + void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent) { if (width != getRect().getWidth() || height != getRect().getHeight()) @@ -3267,17 +3468,17 @@ void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent) LLView::reshape(width, height, called_from_parent); } -BOOL LLMenuHolderGL::hasVisibleMenu() const +LLView* const LLMenuHolderGL::getVisibleMenu() const { for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { LLView* viewp = *child_it; - if (viewp->getVisible() && dynamic_cast<LLMenuBarGL*>(viewp) == NULL) + if (viewp->getVisible() && dynamic_cast<LLMenuGL*>(viewp) != NULL) { - return TRUE; + return viewp; } } - return FALSE; + return NULL; } @@ -3295,8 +3496,7 @@ BOOL LLMenuHolderGL::hideMenus() for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) { LLView* viewp = *child_it; - // clicks off of menu do not hide menu bar - if (dynamic_cast<LLMenuBarGL*>(viewp) == NULL && viewp->getVisible()) + if (dynamic_cast<LLMenuGL*>(viewp) != NULL && viewp->getVisible()) { viewp->setVisible(FALSE); } @@ -3320,9 +3520,9 @@ void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item) /// Class LLTearOffMenu ///============================================================================ LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : - LLFloater() + LLFloater(LLSD()) { - static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0); + S32 floater_header_size = getHeaderHeight(); setName(menup->getName()); setTitle(menup->getLabel()); @@ -3335,28 +3535,37 @@ LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : LLRect rect; menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView); // make sure this floater is big enough for menu - mTargetHeight = (F32)(rect.getHeight() + floater_header_size + 5); + mTargetHeight = (F32)(rect.getHeight() + floater_header_size); reshape(rect.getWidth(), rect.getHeight()); setRect(rect); // attach menu to floater - menup->setFollowsAll(); + menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); mOldParent = menup->getParent(); addChild(menup); menup->setVisible(TRUE); - menup->translate(-menup->getRect().mLeft + 1, -menup->getRect().mBottom + 1); + LLRect menu_rect = menup->getRect(); + menu_rect.setOriginAndSize( 1, 1, + menu_rect.getWidth(), menu_rect.getHeight()); + menup->setRect(menu_rect); menup->setDropShadowed(FALSE); mMenu = menup; // highlight first item (tear off item will be disabled) mMenu->highlightNextItem(NULL); + + // Can't do this in postBuild() because that is only called for floaters + // constructed from XML. + mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this)); } +LLTearOffMenu::~LLTearOffMenu() +{ +} void LLTearOffMenu::draw() { - static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0); mMenu->setBackgroundVisible(isBackgroundOpaque()); mMenu->needsArrange(); @@ -3365,12 +3574,6 @@ void LLTearOffMenu::draw() // animate towards target height reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), mTargetHeight, LLCriticalDamp::getInterpolant(0.05f)))); } - else - { - // when in stasis, remain big enough to hold menu contents - mTargetHeight = (F32)(mMenu->getRect().getHeight() + floater_header_size + 4); - reshape(mMenu->getRect().getWidth() + 3, mMenu->getRect().getHeight() + floater_header_size + 5); - } LLFloater::draw(); } @@ -3452,7 +3655,7 @@ LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup) return tearoffp; } -void LLTearOffMenu::onClose(bool app_quitting) +void LLTearOffMenu::closeTearOff() { removeChild(mMenu); mOldParent->addChild(mMenu); @@ -3462,7 +3665,6 @@ void LLTearOffMenu::onClose(bool app_quitting) mMenu->setVisible(FALSE); mMenu->setTornOff(FALSE); mMenu->setDropShadowed(TRUE); - destroy(); } @@ -3480,6 +3682,11 @@ public: LLContextMenuBranch(const Params&); + virtual ~LLContextMenuBranch() + { + delete mBranch; + } + // called to rebuild the draw label virtual void buildDrawLabel( void ); @@ -3531,16 +3738,19 @@ void LLContextMenuBranch::buildDrawLabel( void ) appendAcceleratorString( st ); mDrawAccelLabel = st; - // No special branch suffix - mDrawBranchLabel.clear(); + mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX; } void LLContextMenuBranch::showSubMenu() { - S32 center_x; - S32 center_y; - localPointToScreen(getRect().getWidth(), getRect().getHeight() , ¢er_x, ¢er_y); - mBranch->show( center_x, center_y, FALSE); + LLMenuItemGL* menu_item = mBranch->getParentMenuItem(); + if (menu_item != NULL && menu_item->getVisible()) + { + S32 center_x; + S32 center_y; + localPointToScreen(getRect().getWidth(), getRect().getHeight() , ¢er_x, ¢er_y); + mBranch->show(center_x, center_y); + } } // onCommit() - do the primary funcationality of the menu item. @@ -3569,7 +3779,7 @@ void LLContextMenuBranch::setHighlight( BOOL highlight ) // class LLContextMenu // A context menu //----------------------------------------------------------------------------- -static LLDefaultWidgetRegistry::Register<LLContextMenu> context_menu_register("context_menu"); +static LLDefaultChildRegistry::Register<LLContextMenu> context_menu_register("context_menu"); static MenuRegistry::Register<LLContextMenu> context_menu_register2("context_menu"); @@ -3577,7 +3787,6 @@ LLContextMenu::LLContextMenu(const Params& p) : LLMenuGL(p), mHoveredAnyItem(FALSE), mHoverItem(NULL) - { //setBackgroundVisible(TRUE); } @@ -3588,8 +3797,15 @@ void LLContextMenu::setVisible(BOOL visible) hide(); } -void LLContextMenu::show(S32 x, S32 y,BOOL adjustCursor) +// Takes cursor position in screen space? +void LLContextMenu::show(S32 x, S32 y) { + // Save click point for detecting cursor moves before mouse-up. + // Must be in local coords to compare with mouseUp events. + // If the mouse doesn't move, the menu will stay open ala the Mac. + // See also LLMenuGL::showPopup() + LLMenuHolderGL::sContextMenuSpawnPos.set(x,y); + arrangeAndClear(); S32 width = getRect().getWidth(); @@ -3597,29 +3813,41 @@ void LLContextMenu::show(S32 x, S32 y,BOOL adjustCursor) const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); LLView* parent_view = getParent(); - if(getParentMenuItem()) + // Open upwards if menu extends past bottom + if (y - height < menu_region_rect.mBottom) { - S32 parent_width = getParentMenuItem()->getRect().getWidth(); - - if(x + width > menu_region_rect.getWidth()) - x -= parent_width + width; + if (getParentMenuItem()) // Adjust if this is a submenu + { + y += height - getParentMenuItem()->getNominalHeight(); + } + else + { + y += height; + } + } + + // Open out to the left if menu extends past right edge + if (x + width > menu_region_rect.mRight) + { + if (getParentMenuItem()) + { + x -= getParentMenuItem()->getRect().getWidth() + width; + } + else + { + x -= width; + } } S32 local_x, local_y; parent_view->screenPointToLocal(x, y, &local_x, &local_y); - // HACK: casting away const. Should use setRect or some helper function instead. - const_cast<LLRect&>(getRect()).setCenterAndSize(local_x + width/2, local_y - height/2, width, height); + LLRect rect; + rect.setLeftTopAndSize(local_x, local_y, width, height); + setRect(rect); arrange(); - - if (translateIntoRect(menu_region_rect,FALSE) && adjustCursor) - { - LLUI::setCursorPositionLocal(getParent(), getRect().mLeft , getRect().mTop); - } - LLView::setVisible(TRUE); - } void LLContextMenu::hide() @@ -3679,58 +3907,8 @@ BOOL LLContextMenu::handleHover( S32 x, S32 y, MASK mask ) return handled; } -BOOL LLContextMenu::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - // The click was somewhere within our rectangle - LLMenuItemGL *item = getHighlightedItem(); - - if (item) - { - // lie to the item about where the click happened - // to make sure it's within the item's rectangle - handled = item->handleMouseDown( 0, 0, mask ); - } - else - { - // call hidemenus to make sure transient selections get cleared - ((LLMenuHolderGL*)getParent())->hideMenus(); - } - - // always handle mouse down as mouse up will close open menus - return handled; -} -BOOL LLContextMenu::handleMouseUp( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - - // The click was somewhere within our rectangle - LLMenuItemGL *item = getHighlightedItem(); - - if (item) - { - // lie to the item about where the click happened - // to make sure it's within the item's rectangle - if (item->getEnabled()) - { - handled = item->handleMouseUp( 0, 0, mask ); - hide(); - } - } - else - { - // call hidemenus to make sure transient selections get cleared - ((LLMenuHolderGL*)getParent())->hideMenus(); - } - - if (!handled) - { - // call hidemenus to make sure transient selections get cleared - sMenuContainer->hideMenus(); - } +// handleMouseDown and handleMouseUp are handled by LLMenuGL - return handled; -} BOOL LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) { @@ -3766,8 +3944,6 @@ BOOL LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask) BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) { - // release mouse capture when right mouse button released, and we're past the shrink time - S32 local_x = x - getRect().mLeft; S32 local_y = y - getRect().mBottom; @@ -3802,14 +3978,13 @@ BOOL LLContextMenu::appendContextSubMenu(LLContextMenu *menu) p.name = menu->getName(); p.label = menu->getLabel(); p.branch = menu; - p.enabled_color=LLUI::getCachedColorFunctor("MenuItemEnabledColor"); - p.disabled_color=LLUI::getCachedColorFunctor("MenuItemDisabledColor"); - p.highlight_bg_color=LLUI::getCachedColorFunctor("MenuItemHighlightBgColor"); - p.highlight_fg_color=LLUI::getCachedColorFunctor("MenuItemHighlightFgColor"); + p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor"); + p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor"); + p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor"); + p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor"); item = LLUICtrlFactory::create<LLContextMenuBranch>(p); LLMenuGL::sMenuContainer->addChild(item->getBranch()); - item->setFont( LLFontGL::getFontSansSerifSmall() ); return append( item ); } |