diff options
Diffstat (limited to 'indra/llui/llfloater.cpp')
-rw-r--r-- | indra/llui/llfloater.cpp | 741 |
1 files changed, 477 insertions, 264 deletions
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 0c0c5921ce..e672252a50 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -61,31 +61,11 @@ #include "lltrans.h" #include "llhelp.h" #include "llmultifloater.h" +#include "llsdutil.h" // use this to control "jumping" behavior when Ctrl-Tabbing const S32 TABBED_FLOATER_OFFSET = 0; -std::string LLFloater::sButtonActiveImageNames[BUTTON_COUNT] = -{ - "Icon_Close_Foreground", //BUTTON_CLOSE - "Icon_Restore_Foreground", //BUTTON_RESTORE - "Icon_Minimize_Foreground", //BUTTON_MINIMIZE - "tearoffbox.tga", //BUTTON_TEAR_OFF - "Icon_Dock_Foreground", //BUTTON_DOCK - "Icon_Undock_Foreground", //BUTTON_UNDOCK - "Icon_Help_Foreground" //BUTTON_HELP -}; - -std::string LLFloater::sButtonPressedImageNames[BUTTON_COUNT] = -{ - "Icon_Close_Press", //BUTTON_CLOSE - "Icon_Restore_Press", //BUTTON_RESTORE - "Icon_Minimize_Press", //BUTTON_MINIMIZE - "tearoff_pressed.tga", //BUTTON_TEAR_OFF - "Icon_Dock_Press", //BUTTON_DOCK - "Icon_Undock_Press", //BUTTON_UNDOCK - "Icon_Help_Press" //BUTTON_HELP -}; std::string LLFloater::sButtonNames[BUTTON_COUNT] = { @@ -94,7 +74,6 @@ std::string LLFloater::sButtonNames[BUTTON_COUNT] = "llfloater_minimize_btn", //BUTTON_MINIMIZE "llfloater_tear_off_btn", //BUTTON_TEAR_OFF "llfloater_dock_btn", //BUTTON_DOCK - "llfloater_undock_btn", //BUTTON_UNDOCK "llfloater_help_btn" //BUTTON_HELP }; @@ -111,7 +90,6 @@ std::string LLFloater::sButtonToolTipsIndex[BUTTON_COUNT]= "BUTTON_MINIMIZE", //"Minimize", //BUTTON_MINIMIZE "BUTTON_TEAR_OFF", //"Tear Off", //BUTTON_TEAR_OFF "BUTTON_DOCK", - "BUTTON_UNDOCK", "BUTTON_HELP" }; @@ -122,7 +100,6 @@ LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] = LLFloater::onClickMinimize, //BUTTON_MINIMIZE LLFloater::onClickTearOff, //BUTTON_TEAR_OFF LLFloater::onClickDock, //BUTTON_DOCK - LLFloater::onClickDock, //BUTTON_UNDOCK LLFloater::onClickHelp //BUTTON_HELP }; @@ -132,6 +109,16 @@ LLFloater::handle_map_t LLFloater::sFloaterMap; LLFloaterView* gFloaterView = NULL; +/*==========================================================================*| +// DEV-38598: The fundamental problem with this operation is that it can only +// support a subset of LLSD values. While it's plausible to compare two arrays +// lexicographically, what strict ordering can you impose on maps? +// (LLFloaterTOS's current key is an LLSD map.) + +// Of course something like this is necessary if you want to build a std::set +// or std::map with LLSD keys. Fortunately we're getting by with other +// container types for now. + //static bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b) { @@ -159,32 +146,11 @@ bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b) else return false; // no valid operation for Binary } +|*==========================================================================*/ bool LLFloater::KeyCompare::equate(const LLSD& a, const LLSD& b) { - if (a.type() != b.type()) - { - //llerrs << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << llendl; - return false; - } - else if (a.isUndefined()) - return true; - else if (a.isInteger()) - return a.asInteger() == b.asInteger(); - else if (a.isReal()) - return a.asReal() == b.asReal(); - else if (a.isString()) - return a.asString() == b.asString(); - else if (a.isUUID()) - return a.asUUID() == b.asUUID(); - else if (a.isDate()) - return a.asDate() == b.asDate(); - else if (a.isURI()) - return a.asString() == b.asString(); // compare URIs as strings - else if (a.isBoolean()) - return a.asBoolean() == b.asBoolean(); - else - return false; // no valid operation for Binary + return llsd_equals(a, b); } //************************************ @@ -199,11 +165,26 @@ LLFloater::Params::Params() can_close("can_close", true), can_drag_on_left("can_drag_on_left", false), can_tear_off("can_tear_off", true), + save_dock_state("save_dock_state", false), save_rect("save_rect", false), save_visibility("save_visibility", false), + can_dock("can_dock", false), + header_height("header_height", 0), + legacy_header_height("legacy_header_height", 0), + close_image("close_image"), + restore_image("restore_image"), + minimize_image("minimize_image"), + tear_off_image("tear_off_image"), + dock_image("dock_image"), + help_image("help_image"), + close_pressed_image("close_pressed_image"), + restore_pressed_image("restore_pressed_image"), + minimize_pressed_image("minimize_pressed_image"), + tear_off_pressed_image("tear_off_pressed_image"), + dock_pressed_image("dock_pressed_image"), + help_pressed_image("help_pressed_image"), open_callback("open_callback"), - close_callback("close_callback"), - can_dock("can_dock", false) + close_callback("close_callback") { visible = false; } @@ -229,7 +210,7 @@ void LLFloater::initClass() static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFloater::Params), "floater"); LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) -: LLPanel(), +: LLPanel(), // intentionally do not pass params here, see initFromParams mDragHandle(NULL), mTitle(p.title), mShortTitle(p.short_title), @@ -243,6 +224,8 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mResizable(p.can_resize), mMinWidth(p.min_width), mMinHeight(p.min_height), + mHeaderHeight(p.header_height), + mLegacyHeaderHeight(p.legacy_header_height), mMinimized(FALSE), mForeground(FALSE), mFirstLook(TRUE), @@ -250,13 +233,14 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mAutoFocus(TRUE), // automatically take focus when opened mCanDock(false), mDocked(false), + mTornOff(false), mHasBeenDraggedWhileMinimized(FALSE), mPreviousMinimizedBottom(0), - mPreviousMinimizedLeft(0), - mNotificationContext(NULL) + mPreviousMinimizedLeft(0) +// mNotificationContext(NULL) { mHandle.bind(this); - mNotificationContext = new LLFloaterNotificationContext(getHandle()); +// mNotificationContext = new LLFloaterNotificationContext(getHandle()); // Clicks stop here. setMouseOpaque(TRUE); @@ -268,32 +252,23 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) // prior rectangle to be used on restore. mExpandedRect.set(0,0,0,0); - for (S32 i = 0; i < BUTTON_COUNT; i++) - { - mButtonsEnabled[i] = FALSE; - mButtons[i] = NULL; - } - for (S32 i = 0; i < 4; i++) - { - mResizeBar[i] = NULL; - mResizeHandle[i] = NULL; - } + memset(mButtonsEnabled, 0, BUTTON_COUNT * sizeof(bool)); + memset(mButtons, 0, BUTTON_COUNT * sizeof(LLButton*)); + + addDragHandle(); + addResizeCtrls(); initFromParams(p); // chrome floaters don't take focus at all setFocusRoot(!getIsChrome()); - initFloater(); + initFloater(p); } // Note: Floaters constructed from XML call init() twice! -void LLFloater::initFloater() +void LLFloater::initFloater(const Params& p) { - addDragHandle(); - - addResizeCtrls(); - // Close button. if (mCanClose) { @@ -317,7 +292,7 @@ void LLFloater::initFloater() mButtonsEnabled[BUTTON_DOCK] = TRUE; } - buildButtons(); + buildButtons(p); // Floaters are created in the invisible state setVisible(FALSE); @@ -333,9 +308,6 @@ void LLFloater::initFloater() void LLFloater::addDragHandle() { - static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0); - S32 close_box_size = mCanClose ? floater_close_box_size : 0; - if (!mDragHandle) { if (mDragOnLeft) @@ -356,6 +328,14 @@ void LLFloater::addDragHandle() } addChild(mDragHandle); } + layoutDragHandle(); +} + +void LLFloater::layoutDragHandle() +{ + static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0); + S32 close_box_size = mCanClose ? floater_close_box_size : 0; + LLRect rect; if (mDragOnLeft) { @@ -366,45 +346,22 @@ void LLFloater::addDragHandle() rect = getLocalRect(); } mDragHandle->setRect(rect); - updateButtons(); + updateTitleButtons(); applyTitle(); } void LLFloater::addResizeCtrls() -{ - for (S32 i = 0; i < 4; i++) - { - if (mResizeBar[i]) - { - removeChild(mResizeBar[i]); - delete mResizeBar[i]; - mResizeBar[i] = NULL; - } - if (mResizeHandle[i]) - { - removeChild(mResizeHandle[i]); - delete mResizeHandle[i]; - mResizeHandle[i] = NULL; - } - } - if( !mResizable ) - { - return; - } - +{ // Resize bars (sides) - const S32 RESIZE_BAR_THICKNESS = 3; LLResizeBar::Params p; p.name("resizebar_left"); p.resizing_view(this); - p.rect(LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0)); p.min_size(mMinWidth); p.side(LLResizeBar::LEFT); mResizeBar[LLResizeBar::LEFT] = LLUICtrlFactory::create<LLResizeBar>(p); addChild( mResizeBar[LLResizeBar::LEFT] ); p.name("resizebar_top"); - p.rect(LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS)); p.min_size(mMinHeight); p.side(LLResizeBar::TOP); @@ -412,15 +369,12 @@ void LLFloater::addResizeCtrls() addChild( mResizeBar[LLResizeBar::TOP] ); p.name("resizebar_right"); - p.rect(LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0)); p.min_size(mMinWidth); - p.side(LLResizeBar::RIGHT); - + p.side(LLResizeBar::RIGHT); mResizeBar[LLResizeBar::RIGHT] = LLUICtrlFactory::create<LLResizeBar>(p); addChild( mResizeBar[LLResizeBar::RIGHT] ); p.name("resizebar_bottom"); - p.rect(LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0)); p.min_size(mMinHeight); p.side(LLResizeBar::BOTTOM); mResizeBar[LLResizeBar::BOTTOM] = LLUICtrlFactory::create<LLResizeBar>(p); @@ -431,27 +385,69 @@ void LLFloater::addResizeCtrls() // handles must not be mouse-opaque, otherwise they block hover events // to other buttons like the close box. JC handle_p.mouse_opaque(false); - handle_p.rect(LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0)); handle_p.min_width(mMinWidth); handle_p.min_height(mMinHeight); handle_p.corner(LLResizeHandle::RIGHT_BOTTOM); mResizeHandle[0] = LLUICtrlFactory::create<LLResizeHandle>(handle_p); addChild(mResizeHandle[0]); - handle_p.rect(LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT)); handle_p.corner(LLResizeHandle::RIGHT_TOP); mResizeHandle[1] = LLUICtrlFactory::create<LLResizeHandle>(handle_p); addChild(mResizeHandle[1]); - handle_p.rect(LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 )); handle_p.corner(LLResizeHandle::LEFT_BOTTOM); mResizeHandle[2] = LLUICtrlFactory::create<LLResizeHandle>(handle_p); addChild(mResizeHandle[2]); - handle_p.rect(LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT )); handle_p.corner(LLResizeHandle::LEFT_TOP); mResizeHandle[3] = LLUICtrlFactory::create<LLResizeHandle>(handle_p); addChild(mResizeHandle[3]); + + layoutResizeCtrls(); +} + +void LLFloater::layoutResizeCtrls() +{ + LLRect rect; + + // Resize bars (sides) + const S32 RESIZE_BAR_THICKNESS = 3; + rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0); + mResizeBar[LLResizeBar::LEFT]->setRect(rect); + + rect = LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS); + mResizeBar[LLResizeBar::TOP]->setRect(rect); + + rect = LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0); + mResizeBar[LLResizeBar::RIGHT]->setRect(rect); + + rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0); + mResizeBar[LLResizeBar::BOTTOM]->setRect(rect); + + // Resize handles (corners) + rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0); + mResizeHandle[0]->setRect(rect); + + rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT); + mResizeHandle[1]->setRect(rect); + + rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ); + mResizeHandle[2]->setRect(rect); + + rect = LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT ); + mResizeHandle[3]->setRect(rect); +} + +void LLFloater::enableResizeCtrls(bool enable) +{ + for (S32 i = 0; i < 4; ++i) + { + mResizeBar[i]->setVisible(enable); + mResizeBar[i]->setEnabled(enable); + + mResizeHandle[i]->setVisible(enable); + mResizeHandle[i]->setEnabled(enable); + } } // virtual @@ -459,8 +455,8 @@ LLFloater::~LLFloater() { LLFloaterReg::removeInstance(mInstanceName, mKey); - delete mNotificationContext; - mNotificationContext = NULL; +// delete mNotificationContext; +// mNotificationContext = NULL; //// am I not hosted by another floater? //if (mHostHandle.isDead()) @@ -493,6 +489,7 @@ LLFloater::~LLFloater() storeRectControl(); setVisible(false); // We're not visible if we're destroyed storeVisibilityControl(); + storeDockStateControl(); } void LLFloater::storeRectControl() @@ -511,6 +508,15 @@ void LLFloater::storeVisibilityControl() } } +void LLFloater::storeDockStateControl() +{ + if( !sQuitting && mDocStateControl.size() > 1 ) + { + LLUI::sSettingGroups["floater"]->setBOOL( mDocStateControl, isDocked() ); + } +} + + void LLFloater::setVisible( BOOL visible ) { LLPanel::setVisible(visible); // calls handleVisibilityChange() @@ -521,10 +527,7 @@ void LLFloater::setVisible( BOOL visible ) if( !visible ) { - if( gFocusMgr.childIsTopCtrl( this ) ) - { - gFocusMgr.setTopCtrl(NULL); - } + LLUI::removePopup(this); if( gFocusMgr.childHasMouseCapture( this ) ) { @@ -698,10 +701,7 @@ void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent) void LLFloater::releaseFocus() { - if( gFocusMgr.childIsTopCtrl( this ) ) - { - gFocusMgr.setTopCtrl(NULL); - } + LLUI::removePopup(this); setFocus(FALSE); @@ -753,6 +753,12 @@ LLMultiFloater* LLFloater::getHost() return (LLMultiFloater*)mHostHandle.get(); } +void LLFloater::applySavedVariables() +{ + applyRectControl(); + applyDockState(); +} + void LLFloater::applyRectControl() { if (mRectControl.size() > 1) @@ -769,6 +775,16 @@ void LLFloater::applyRectControl() } } +void LLFloater::applyDockState() +{ + if (mDocStateControl.size() > 1) + { + bool dockState = LLUI::sSettingGroups["floater"]->getBOOL(mDocStateControl); + setDocked(dockState); + } + +} + void LLFloater::applyTitle() { if (!mDragHandle) @@ -857,9 +873,11 @@ void LLFloater::setSnappedTo(const LLView* snap_view) else { //RN: assume it's a floater as it must be a sibling to our parent floater - LLFloater* floaterp = (LLFloater*)snap_view; - - setSnapTarget(floaterp->getHandle()); + const LLFloater* floaterp = dynamic_cast<const LLFloater*>(snap_view); + if (floaterp) + { + setSnapTarget(floaterp->getHandle()); + } } } @@ -919,7 +937,8 @@ void LLFloater::handleReshape(const LLRect& new_rect, bool by_user) void LLFloater::setMinimized(BOOL minimize) { - static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0); + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0); if (minimize == mMinimized) return; @@ -1042,15 +1061,10 @@ void LLFloater::setMinimized(BOOL minimize) // Reshape *after* setting mMinimized reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE ); } - - // don't show the help button while minimized - it's - // not very useful when minimized and uses up space - mButtonsEnabled[BUTTON_HELP] = !minimize; - - applyTitle (); make_ui_sound("UISndWindowClose"); - updateButtons(); + updateTitleButtons(); + applyTitle (); } void LLFloater::setFocus( BOOL b ) @@ -1092,7 +1106,8 @@ void LLFloater::setFocus( BOOL b ) void LLFloater::setRect(const LLRect &rect) { LLPanel::setRect(rect); - addDragHandle(); // re-add drag handle, sized based on rect + layoutDragHandle(); + layoutResizeCtrls(); } // virtual @@ -1105,6 +1120,7 @@ void LLFloater::setIsChrome(BOOL is_chrome) setFocus(FALSE); // can't Ctrl-Tab to "chrome" floaters setFocusRoot(FALSE); + mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome))); } // no titles displayed on "chrome" floaters @@ -1174,7 +1190,7 @@ void LLFloater::setHost(LLMultiFloater* host) mButtonScale = 1.f; //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE; } - updateButtons(); + updateTitleButtons(); if (host) { mHostHandle = host->getHandle(); @@ -1243,7 +1259,7 @@ void LLFloater::removeDependentFloater(LLFloater* floaterp) floaterp->mDependeeHandle = LLHandle<LLFloater>(); } -BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButtons index) +BOOL LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index) { if( mButtonsEnabled[index] ) { @@ -1367,15 +1383,13 @@ void LLFloater::setCanDock(bool b) if(mCanDock) { mButtonsEnabled[BUTTON_DOCK] = !mDocked; - mButtonsEnabled[BUTTON_UNDOCK] = mDocked; } else { mButtonsEnabled[BUTTON_DOCK] = FALSE; - mButtonsEnabled[BUTTON_UNDOCK] = FALSE; } } - updateButtons(); + updateTitleButtons(); } void LLFloater::setDocked(bool docked, bool pop_on_undock) @@ -1384,9 +1398,11 @@ void LLFloater::setDocked(bool docked, bool pop_on_undock) { mDocked = docked; mButtonsEnabled[BUTTON_DOCK] = !mDocked; - mButtonsEnabled[BUTTON_UNDOCK] = mDocked; - updateButtons(); + updateTitleButtons(); + + storeDockStateControl(); } + } // static @@ -1399,9 +1415,9 @@ void LLFloater::onClickMinimize(LLFloater* self) void LLFloater::onClickTearOff(LLFloater* self) { - static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0); if (!self) return; + S32 floater_header_size = self->mHeaderHeight; LLMultiFloater* host_floater = self->getHost(); if (host_floater) //Tear off { @@ -1421,6 +1437,7 @@ void LLFloater::onClickTearOff(LLFloater* self) gFloaterView->adjustToFitScreen(self, FALSE); // give focus to new window to keep continuity for the user self->setFocus(TRUE); + self->setTornOff(true); } else //Attach to parent. { @@ -1432,7 +1449,9 @@ void LLFloater::onClickTearOff(LLFloater* self) // make sure host is visible new_host->openFloater(new_host->getKey()); } + self->setTornOff(false); } + self->updateTitleButtons(); } // static @@ -1528,7 +1547,12 @@ void LLFloater::onClickClose( LLFloater* self ) { if (!self) return; - self->closeFloater(false); + self->onClickCloseBtn(); +} + +void LLFloater::onClickCloseBtn() +{ + closeFloater(false); } @@ -1539,45 +1563,52 @@ void LLFloater::draw() // draw background if( isBackgroundVisible() ) { + drawShadow(this); + S32 left = LLPANEL_BORDER_WIDTH; S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH; S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH; S32 bottom = LLPANEL_BORDER_WIDTH; - static LLUICachedControl<S32> shadow_offset_S32 ("DropShadowFloater", 0); - static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow"); - LLColor4 shadow_color = shadow_color_cached; - F32 shadow_offset = (F32)shadow_offset_S32; - - if (!isBackgroundOpaque()) - { - shadow_offset *= 0.2f; - shadow_color.mV[VALPHA] *= 0.5f; - } - gl_drop_shadow(left, top, right, bottom, - shadow_color % alpha, - llround(shadow_offset)); - - // No transparent windows in simple UI + LLUIImage* image = NULL; + LLColor4 color; + LLColor4 overlay_color; if (isBackgroundOpaque()) { - gl_rect_2d( left, top, right, bottom, getBackgroundColor() % alpha ); + // NOTE: image may not be set + image = getBackgroundImage(); + color = getBackgroundColor(); + overlay_color = getBackgroundImageOverlay(); } else { - gl_rect_2d( left, top, right, bottom, getTransparentColor() % alpha ); + image = getTransparentImage(); + color = getTransparentColor(); + overlay_color = getTransparentImageOverlay(); } - if(hasFocus() - && !getIsChrome() - && !getCurrentTitle().empty()) + if (image) + { + // We're using images for this floater's backgrounds + image->draw(getLocalRect(), overlay_color % alpha); + } + else { - static LLUIColor titlebar_focus_color = LLUIColorTable::instance().getColor("TitleBarFocusColor"); + // We're not using images, use old-school flat colors + gl_rect_2d( left, top, right, bottom, color % alpha ); + // draw highlight on title bar to indicate focus. RDW - const LLFontGL* font = LLFontGL::getFontSansSerif(); - LLRect r = getRect(); - gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, - titlebar_focus_color % alpha, 0, TRUE); + if(hasFocus() + && !getIsChrome() + && !getCurrentTitle().empty()) + { + static LLUIColor titlebar_focus_color = LLUIColorTable::instance().getColor("TitleBarFocusColor"); + + const LLFontGL* font = LLFontGL::getFontSansSerif(); + LLRect r = getRect(); + gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, + titlebar_focus_color % alpha, 0, TRUE); + } } } @@ -1608,35 +1639,8 @@ void LLFloater::draw() } else { - // draw children - LLView* focused_child = dynamic_cast<LLView*>(gFocusMgr.getKeyboardFocus()); - BOOL focused_child_visible = FALSE; - if (focused_child && focused_child->getParent() == this) - { - focused_child_visible = focused_child->getVisible(); - focused_child->setVisible(FALSE); - } - // don't call LLPanel::draw() since we've implemented custom background rendering LLView::draw(); - - if (focused_child_visible) - { - focused_child->setVisible(TRUE); - } - drawChild(focused_child); - } - - if( isBackgroundVisible() ) - { - // add in a border to improve spacialized visual aclarity ;) - // use lines instead of gl_rect_2d so we can round the edges as per james' recommendation - static LLUIColor focus_border_color = LLUIColorTable::instance().getColor("FloaterFocusBorderColor"); - static LLUIColor unfocus_border_color = LLUIColorTable::instance().getColor("FloaterUnfocusBorderColor"); - LLUI::setLineWidth(1.5f); - LLColor4 outlineColor = gFocusMgr.childHasKeyboardFocus(this) ? focus_border_color : unfocus_border_color; - gl_rect_2d_offset_local(0, getRect().getHeight() + 1, getRect().getWidth() + 1, 0, outlineColor % alpha, -LLPANEL_BORDER_WIDTH, FALSE); - LLUI::setLineWidth(1.f); } // update tearoff button for torn off floaters @@ -1651,6 +1655,29 @@ void LLFloater::draw() } } +void LLFloater::drawShadow(LLPanel* panel) +{ + F32 alpha = panel->getDrawContext().mAlpha; + S32 left = LLPANEL_BORDER_WIDTH; + S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH; + S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH; + S32 bottom = LLPANEL_BORDER_WIDTH; + + static LLUICachedControl<S32> shadow_offset_S32 ("DropShadowFloater", 0); + static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow"); + LLColor4 shadow_color = shadow_color_cached; + F32 shadow_offset = (F32)shadow_offset_S32; + + if (!panel->isBackgroundOpaque()) + { + shadow_offset *= 0.2f; + shadow_color.mV[VALPHA] *= 0.5f; + } + gl_drop_shadow(left, top, right, bottom, + shadow_color % alpha, + llround(shadow_offset)); +} + void LLFloater::setCanMinimize(BOOL can_minimize) { // if removing minimize/restore button programmatically, @@ -1664,7 +1691,7 @@ void LLFloater::setCanMinimize(BOOL can_minimize) mButtonsEnabled[BUTTON_MINIMIZE] = can_minimize && !isMinimized(); mButtonsEnabled[BUTTON_RESTORE] = can_minimize && isMinimized(); - updateButtons(); + updateTitleButtons(); } void LLFloater::setCanClose(BOOL can_close) @@ -1672,7 +1699,7 @@ void LLFloater::setCanClose(BOOL can_close) mCanClose = can_close; mButtonsEnabled[BUTTON_CLOSE] = can_close; - updateButtons(); + updateTitleButtons(); } void LLFloater::setCanTearOff(BOOL can_tear_off) @@ -1680,14 +1707,14 @@ void LLFloater::setCanTearOff(BOOL can_tear_off) mCanTearOff = can_tear_off; mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead(); - updateButtons(); + updateTitleButtons(); } void LLFloater::setCanResize(BOOL can_resize) { mResizable = can_resize; - addResizeCtrls(); + enableResizeCtrls(can_resize); } void LLFloater::setCanDrag(BOOL can_drag) @@ -1704,21 +1731,40 @@ void LLFloater::setCanDrag(BOOL can_drag) } } -void LLFloater::updateButtons() +void LLFloater::updateTitleButtons() { static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0); static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0); + LLRect buttons_rect; S32 button_count = 0; for (S32 i = 0; i < BUTTON_COUNT; i++) { - if(!mButtons[i]) continue; - mButtons[i]->setEnabled(mButtonsEnabled[i]); + if (!mButtons[i]) + { + continue; + } + + bool enabled = mButtonsEnabled[i]; + if (i == BUTTON_HELP) + { + // don't show the help button if the floater is minimized + // or if it is a docked tear-off floater + if (isMinimized() || (mButtonsEnabled[BUTTON_TEAR_OFF] && ! mTornOff)) + { + enabled = false; + } + } + if (i == BUTTON_CLOSE && mButtonScale != 1.f) + { + //*HACK: always render close button for hosted floaters so + //that users don't accidentally hit the button when + //closing multiple windows in the chatterbox + enabled = true; + } + + mButtons[i]->setEnabled(enabled); - if (mButtonsEnabled[i] - //*HACK: always render close button for hosted floaters - // so that users don't accidentally hit the button when closing multiple windows - // in the chatterbox - || (i == BUTTON_CLOSE && mButtonScale != 1.f)) + if (enabled) { button_count++; @@ -1740,21 +1786,33 @@ void LLFloater::updateButtons() llround((F32)floater_close_box_size * mButtonScale)); } + if(!buttons_rect.isValid()) + { + buttons_rect = btn_rect; + } + else + { + mDragOnLeft ? buttons_rect.mRight + btn_rect.mRight : + buttons_rect.mLeft = btn_rect.mLeft; + } mButtons[i]->setRect(btn_rect); mButtons[i]->setVisible(TRUE); // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater mButtons[i]->setTabStop(i == BUTTON_RESTORE); } - else if (mButtons[i]) + else { mButtons[i]->setVisible(FALSE); } } if (mDragHandle) - mDragHandle->setMaxTitleWidth(getRect().getWidth() - (button_count * (floater_close_box_size + 1))); + { + localRectToOtherView(buttons_rect, &buttons_rect, mDragHandle); + mDragHandle->setButtonsRect(buttons_rect); + } } -void LLFloater::buildButtons() +void LLFloater::buildButtons(const Params& floater_params) { static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0); static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0); @@ -1788,36 +1846,96 @@ void LLFloater::buildButtons() LLButton::Params p; p.name(sButtonNames[i]); p.rect(btn_rect); - p.label(""); - p.image_unselected.name(sButtonActiveImageNames[i]); + p.image_unselected = getButtonImage(floater_params, (EFloaterButton)i); // Selected, no matter if hovered or not, is "pressed" - p.image_selected.name(sButtonPressedImageNames[i]); - p.image_hover_selected.name(sButtonPressedImageNames[i]); + LLUIImage* pressed_image = getButtonPressedImage(floater_params, (EFloaterButton)i); + p.image_selected = pressed_image; + p.image_hover_selected = pressed_image; // Use a glow effect when the user hovers over the button // These icons are really small, need glow amount increased p.hover_glow_amount( 0.33f ); p.click_callback.function(boost::bind(sButtonCallbacks[i], this)); p.tab_stop(false); p.follows.flags(FOLLOWS_TOP|FOLLOWS_RIGHT); - p.tool_tip(sButtonToolTips[i]); + p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i, getIsChrome()); p.scale_image(true); + p.chrome(true); LLButton* buttonp = LLUICtrlFactory::create<LLButton>(p); addChild(buttonp); mButtons[i] = buttonp; } - updateButtons(); + updateTitleButtons(); +} + +// static +LLUIImage* LLFloater::getButtonImage(const Params& p, EFloaterButton e) +{ + switch(e) + { + default: + case BUTTON_CLOSE: + return p.close_image; + case BUTTON_RESTORE: + return p.restore_image; + case BUTTON_MINIMIZE: + return p.minimize_image; + case BUTTON_TEAR_OFF: + return p.tear_off_image; + case BUTTON_DOCK: + return p.dock_image; + case BUTTON_HELP: + return p.help_image; + } +} + +// static +LLUIImage* LLFloater::getButtonPressedImage(const Params& p, EFloaterButton e) +{ + switch(e) + { + default: + case BUTTON_CLOSE: + return p.close_pressed_image; + case BUTTON_RESTORE: + return p.restore_pressed_image; + case BUTTON_MINIMIZE: + return p.minimize_pressed_image; + case BUTTON_TEAR_OFF: + return p.tear_off_pressed_image; + case BUTTON_DOCK: + return p.dock_pressed_image; + case BUTTON_HELP: + return p.help_pressed_image; + } +} + +// static +std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome) +{ + // EXT-4081 (Lag Meter: Ctrl+W does not close floater) + // If floater is chrome set 'Close' text for close button's tooltip + if(is_chrome && BUTTON_CLOSE == e) + { + static std::string close_tooltip_chrome = LLTrans::getString("BUTTON_CLOSE_CHROME"); + return close_tooltip_chrome; + } + // TODO: per-floater localizable tooltips set in XML + return sButtonToolTips[e]; } ///////////////////////////////////////////////////// // LLFloaterView +static LLDefaultChildRegistry::Register<LLFloaterView> r("floater_view"); + LLFloaterView::LLFloaterView (const Params& p) : LLUICtrl (p), + mFocusCycleMode(FALSE), - mSnapOffsetBottom(0) - ,mSnapOffsetRight(0) + mSnapOffsetBottom(0), + mSnapOffsetRight(0) { } @@ -1842,41 +1960,50 @@ void LLFloaterView::reshapeFloater(S32 width, S32 height, BOOL called_from_paren // dependents use same follow flags as their "dependee" continue; } - LLRect r = floaterp->getRect(); - - // Compute absolute distance from each edge of screen - S32 left_offset = llabs(r.mLeft - 0); - S32 right_offset = llabs(old_width - r.mRight); - - S32 top_offset = llabs(old_height - r.mTop); - S32 bottom_offset = llabs(r.mBottom - 0); // Make if follow the edge it is closest to U32 follow_flags = 0x0; - if (left_offset < right_offset) + if (floaterp->isMinimized()) { - follow_flags |= FOLLOWS_LEFT; + follow_flags |= (FOLLOWS_LEFT | FOLLOWS_TOP); } else { - follow_flags |= FOLLOWS_RIGHT; - } + LLRect r = floaterp->getRect(); - // "No vertical adjustment" usually means that the bottom of the view - // has been pushed up or down. Hence we want the floaters to follow - // the top. - if (!adjust_vertical) - { - follow_flags |= FOLLOWS_TOP; - } - else if (top_offset < bottom_offset) - { - follow_flags |= FOLLOWS_TOP; - } - else - { - follow_flags |= FOLLOWS_BOTTOM; + // Compute absolute distance from each edge of screen + S32 left_offset = llabs(r.mLeft - 0); + S32 right_offset = llabs(old_width - r.mRight); + + S32 top_offset = llabs(old_height - r.mTop); + S32 bottom_offset = llabs(r.mBottom - 0); + + + if (left_offset < right_offset) + { + follow_flags |= FOLLOWS_LEFT; + } + else + { + follow_flags |= FOLLOWS_RIGHT; + } + + // "No vertical adjustment" usually means that the bottom of the view + // has been pushed up or down. Hence we want the floaters to follow + // the top. + if (!adjust_vertical) + { + follow_flags |= FOLLOWS_TOP; + } + else if (top_offset < bottom_offset) + { + follow_flags |= FOLLOWS_TOP; + } + else + { + follow_flags |= FOLLOWS_BOTTOM; + } } floaterp->setFollows(follow_flags); @@ -2057,6 +2184,11 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus) if (give_focus && !gFocusMgr.childHasKeyboardFocus(child)) { child->setFocus(TRUE); + // floater did not take focus, so relinquish focus to world + if (!child->hasFocus()) + { + gFocusMgr.setKeyboardFocus(NULL); + } } } @@ -2123,18 +2255,19 @@ void LLFloaterView::focusFrontFloater() void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom) { - static LLUICachedControl<S32> floater_header_size ("UIFloaterHeaderSize", 0); + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0); - S32 col = 0; LLRect snap_rect_local = getLocalSnapRect(); - for(S32 row = snap_rect_local.mBottom; - row < snap_rect_local.getHeight() - floater_header_size; - row += floater_header_size ) //loop rows - { - for(col = snap_rect_local.mLeft; - col < snap_rect_local.getWidth() - minimized_width; - col += minimized_width) + for(S32 col = snap_rect_local.mLeft; + col < snap_rect_local.getWidth() - minimized_width; + col += minimized_width) + { + for(S32 row = snap_rect_local.mTop - floater_header_size; + row > floater_header_size; + row -= floater_header_size ) //loop rows { + bool foundGap = TRUE; for(child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); @@ -2246,9 +2379,8 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out } LLRect::tCoordType screen_width = getSnapRect().getWidth(); LLRect::tCoordType screen_height = getSnapRect().getHeight(); - // convert to local coordinate frame - LLRect snap_rect_local = getLocalSnapRect(); + // only automatically resize non-minimized, resizable floaters if( floater->isResizable() && !floater->isMinimized() ) { @@ -2273,7 +2405,11 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out new_width = llmax(new_width, min_width); new_height = llmax(new_height, min_height); - floater->reshape( new_width, new_height, TRUE ); + LLRect new_rect; + new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height); + + floater->setShape(new_rect); + if (floater->followsRight()) { floater->translate(old_width - new_width, 0); @@ -2287,7 +2423,7 @@ void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_out } // move window fully onscreen - if (floater->translateIntoRect( snap_rect_local, allow_partial_outside )) + if (floater->translateIntoRect( getLocalRect(), allow_partial_outside )) { floater->clearSnapTarget(); } @@ -2385,10 +2521,7 @@ void LLFloaterView::syncFloaterTabOrder() if (modal_dialog) { // If we have a visible modal dialog, make sure that it has focus - if( gFocusMgr.getTopCtrl() != modal_dialog ) - { - gFocusMgr.setTopCtrl( modal_dialog ); - } + LLUI::addPopup(modal_dialog); if( !gFocusMgr.childHasKeyboardFocus( modal_dialog ) ) { @@ -2466,12 +2599,18 @@ void LLFloaterView::pushVisibleAll(BOOL visible, const skip_list_t& skip_list) view->pushVisible(visible); } } + + LLFloaterReg::blockShowFloaters(true); } void LLFloaterView::popVisibleAll(const skip_list_t& skip_list) { - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) + // make a copy of the list since some floaters change their + // order in the childList when changing visibility. + child_list_t child_list_copy = *getChildList(); + + for (child_list_const_iter_t child_iter = child_list_copy.begin(); + child_iter != child_list_copy.end(); ++child_iter) { LLView *view = *child_iter; if (skip_list.find(view) == skip_list.end()) @@ -2479,6 +2618,8 @@ void LLFloaterView::popVisibleAll(const skip_list_t& skip_list) view->popVisible(); } } + + LLFloaterReg::blockShowFloaters(false); } void LLFloater::setInstanceName(const std::string& name) @@ -2498,6 +2639,11 @@ void LLFloater::setInstanceName(const std::string& name) { mVisibilityControl = LLFloaterReg::declareVisibilityControl(mInstanceName); } + if(!mDocStateControl.empty()) + { + mDocStateControl = LLFloaterReg::declareDockStateControl(mInstanceName); + } + } } @@ -2538,6 +2684,9 @@ void LLFloater::setupParamsForExport(Params& p, LLView* parent) void LLFloater::initFromParams(const LLFloater::Params& p) { + // *NOTE: We have too many classes derived from LLFloater to retrofit them + // all to pass in params via constructors. So we use this method. + // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible LLPanel::initFromParams(p); @@ -2549,11 +2698,12 @@ void LLFloater::initFromParams(const LLFloater::Params& p) setCanMinimize(p.can_minimize); setCanClose(p.can_close); setCanDock(p.can_dock); + setCanResize(p.can_resize); + setResizeLimits(p.min_width, p.min_height); mDragOnLeft = p.can_drag_on_left; - mResizable = p.can_resize; - mMinWidth = p.min_width; - mMinHeight = p.min_height; + mHeaderHeight = p.header_height; + mLegacyHeaderHeight = p.legacy_header_height; mSingleInstance = p.single_instance; mAutoTile = p.auto_tile; @@ -2565,13 +2715,22 @@ void LLFloater::initFromParams(const LLFloater::Params& p) { mVisibilityControl = "t"; // flag to build mVisibilityControl name once mInstanceName is set } + + if(p.save_dock_state) + { + mDocStateControl = "t"; // flag to build mDocStateControl name once mInstanceName is set + } // open callback if (p.open_callback.isProvided()) - initCommitCallback(p.open_callback, mOpenSignal); + { + mOpenSignal.connect(initCommitCallback(p.open_callback)); + } // close callback if (p.close_callback.isProvided()) - initCommitCallback(p.close_callback, mCloseSignal); + { + mCloseSignal.connect(initCommitCallback(p.close_callback)); + } } LLFastTimer::DeclareTimer POST_BUILD("Floater Post Build"); @@ -2591,10 +2750,23 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o output_node, output_params, &default_params); } - setupParams(params, parent); + // Default floater position to top-left corner of screen + // However, some legacy floaters have explicit top or bottom + // coordinates set, so respect their wishes. + if (!params.rect.top.isProvided() && !params.rect.bottom.isProvided()) + { + params.rect.top.set(0); + } + if (!params.rect.left.isProvided() && !params.rect.right.isProvided()) + { + params.rect.left.set(0); + } + + params.from_xui = true; + applyXUILayout(params, parent); initFromParams(params); - initFloater(); + initFloater(params); LLMultiFloater* last_host = LLFloater::getFloaterHost(); if (node->hasName("multi_floater")) @@ -2609,6 +2781,23 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o LLFloater::setFloaterHost(last_host); } + // HACK: When we changed the header height to 25 pixels in Viewer 2, rather + // than re-layout all the floaters we use this value in pixels to make the + // whole floater bigger and change the top-left coordinate for widgets. + // The goal is to eventually set mLegacyHeaderHeight to zero, which would + // make the top-left corner for widget layout the same as the top-left + // corner of the window's content area. James + S32 header_stretch = (mHeaderHeight - mLegacyHeaderHeight); + if (header_stretch > 0) + { + // Stretch the floater vertically, don't move widgets + LLRect rect = getRect(); + rect.mTop += header_stretch; + + // This will also update drag handle, title bar, close box, etc. + setRect(rect); + } + BOOL result; { LLFastTimer ft(POST_BUILD); @@ -2626,6 +2815,30 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o moveResizeHandlesToFront(); + applyDockState(); + return true; // *TODO: Error checking } +bool LLFloater::isShown() const +{ + return ! isMinimized() && isInVisibleChain(); +} + +/* static */ +bool LLFloater::isShown(const LLFloater* floater) +{ + return floater && floater->isShown(); +} + +/* static */ +bool LLFloater::isMinimized(const LLFloater* floater) +{ + return floater && floater->isMinimized(); +} + +/* static */ +bool LLFloater::isVisible(const LLFloater* floater) +{ + return floater && floater->getVisible(); +} |