/** * @file llfocusmgr.cpp * @brief LLFocusMgr base class * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "llfocusmgr.h" #include "lluictrl.h" #include "v4color.h" const F32 FOCUS_FADE_TIME = 0.3f; LLFocusableElement::LLFocusableElement() : mFocusLostCallback(NULL), mFocusReceivedCallback(NULL), mFocusChangedCallback(NULL), mTopLostCallback(NULL) { } // virtual bool LLFocusableElement::handleKey(KEY key, MASK mask, bool called_from_parent) { return false; } // virtual bool LLFocusableElement::handleKeyUp(KEY key, MASK mask, bool called_from_parent) { return false; } // virtual bool LLFocusableElement::handleUnicodeChar(llwchar uni_char, bool called_from_parent) { return false; } // virtual bool LLFocusableElement::wantsKeyUpKeyDown() const { return false; } //virtual bool LLFocusableElement::wantsReturnKey() const { return false; } // virtual LLFocusableElement::~LLFocusableElement() { auto free_signal = [&](focus_signal_t*& signal) { if (signal) { signal->disconnect_all_slots(); delete signal; signal = nullptr; } }; free_signal(mFocusLostCallback); free_signal(mFocusReceivedCallback); free_signal(mFocusChangedCallback); free_signal(mTopLostCallback); } void LLFocusableElement::onFocusReceived() { if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this); if (mFocusChangedCallback) (*mFocusChangedCallback)(this); } void LLFocusableElement::onFocusLost() { if (mFocusLostCallback) (*mFocusLostCallback)(this); if (mFocusChangedCallback) (*mFocusChangedCallback)(this); } void LLFocusableElement::onTopLost() { if (mTopLostCallback) (*mTopLostCallback)(this); } bool LLFocusableElement::hasFocus() const { return gFocusMgr.getKeyboardFocus() == this; } void LLFocusableElement::setFocus(bool b) { } boost::signals2::connection LLFocusableElement::setFocusLostCallback( const focus_signal_t::slot_type& cb) { if (!mFocusLostCallback) mFocusLostCallback = new focus_signal_t(); return mFocusLostCallback->connect(cb); } boost::signals2::connection LLFocusableElement::setFocusReceivedCallback(const focus_signal_t::slot_type& cb) { if (!mFocusReceivedCallback) mFocusReceivedCallback = new focus_signal_t(); return mFocusReceivedCallback->connect(cb); } boost::signals2::connection LLFocusableElement::setFocusChangedCallback(const focus_signal_t::slot_type& cb) { if (!mFocusChangedCallback) mFocusChangedCallback = new focus_signal_t(); return mFocusChangedCallback->connect(cb); } boost::signals2::connection LLFocusableElement::setTopLostCallback(const focus_signal_t::slot_type& cb) { if (!mTopLostCallback) mTopLostCallback = new focus_signal_t(); return mTopLostCallback->connect(cb); } typedef std::list > view_handle_list_t; typedef std::map, LLHandle > focus_history_map_t; struct LLFocusMgr::Impl { // caching list of keyboard focus ancestors for calling onFocusReceived and onFocusLost view_handle_list_t mCachedKeyboardFocusList; focus_history_map_t mFocusHistory; }; LLFocusMgr gFocusMgr; LLFocusMgr::LLFocusMgr() : mLockedView( NULL ), mMouseCaptor( NULL ), mKeyboardFocus( NULL ), mLastKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), mKeystrokesOnly(false), mTopCtrl( NULL ), mAppHasFocus(true), // Macs don't seem to notify us that we've gotten focus, so default to true mImpl(new LLFocusMgr::Impl) { } LLFocusMgr::~LLFocusMgr() { mImpl->mFocusHistory.clear(); delete mImpl; mImpl = NULL; } void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) { if( childHasMouseCapture( view ) ) { setMouseCapture( NULL ); } if( childHasKeyboardFocus( view )) { if (view == mLockedView) { mLockedView = NULL; setKeyboardFocus( NULL ); } else { setKeyboardFocus( mLockedView ); } } if(LLUI::instanceExists()) LLUI::getInstance()->removePopup(view); } void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, bool lock, bool keystrokes_only) { // notes if keyboard focus is changed again (by onFocusLost/onFocusReceived) // making the rest of our processing unnecessary since it will already be // handled by the recursive call static bool focus_dirty; focus_dirty = false; if (mLockedView && (new_focus == NULL || (new_focus != mLockedView && dynamic_cast(new_focus) && !dynamic_cast(new_focus)->hasAncestor(mLockedView)))) { // don't allow focus to go to anything that is not the locked focus // or one of its descendants return; } mKeystrokesOnly = keystrokes_only; if( new_focus != mKeyboardFocus ) { mLastKeyboardFocus = mKeyboardFocus; mKeyboardFocus = new_focus; // list of the focus and it's ancestors view_handle_list_t old_focus_list = mImpl->mCachedKeyboardFocusList; view_handle_list_t new_focus_list; // walk up the tree to root and add all views to the new_focus_list for (LLView* ctrl = dynamic_cast(mKeyboardFocus); ctrl; ctrl = ctrl->getParent()) { new_focus_list.push_back(ctrl->getHandle()); } // remove all common ancestors since their focus is unchanged while (!new_focus_list.empty() && !old_focus_list.empty() && new_focus_list.back() == old_focus_list.back()) { new_focus_list.pop_back(); old_focus_list.pop_back(); } // walk up the old focus branch calling onFocusLost // we bubble up the tree to release focus, and back down to add for (view_handle_list_t::iterator old_focus_iter = old_focus_list.begin(); old_focus_iter != old_focus_list.end() && !focus_dirty; old_focus_iter++) { LLView* old_focus_view = old_focus_iter->get(); if (old_focus_view) { mImpl->mCachedKeyboardFocusList.pop_front(); old_focus_view->onFocusLost(); } } // walk down the new focus branch calling onFocusReceived for (view_handle_list_t::reverse_iterator new_focus_riter = new_focus_list.rbegin(); new_focus_riter != new_focus_list.rend() && !focus_dirty; new_focus_riter++) { LLView* new_focus_view = new_focus_riter->get(); if (new_focus_view) { mImpl->mCachedKeyboardFocusList.push_front(new_focus_view->getHandle()); new_focus_view->onFocusReceived(); } } // if focus was changed as part of an onFocusLost or onFocusReceived call // stop iterating on current list since it is now invalid if (focus_dirty) { return; } // If we've got a default keyboard focus, and the caller is // releasing keyboard focus, move to the default. if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL) { mDefaultKeyboardFocus->setFocus(true); } LLView* focus_subtree = dynamic_cast(mKeyboardFocus); LLView* viewp = dynamic_cast(mKeyboardFocus); // find root-most focus root while(viewp) { if (viewp->isFocusRoot()) { focus_subtree = viewp; } viewp = viewp->getParent(); } if (focus_subtree) { LLView* focused_view = dynamic_cast(mKeyboardFocus); mImpl->mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle(); } } if (lock) { lockFocus(); } focus_dirty = true; } // Returns true is parent or any descedent of parent has keyboard focus. bool LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const { LLView* focus_view = dynamic_cast(mKeyboardFocus); while( focus_view ) { if( focus_view == parent ) { return true; } focus_view = focus_view->getParent(); } return false; } // Returns true is parent or any descedent of parent is the mouse captor. bool LLFocusMgr::childHasMouseCapture( const LLView* parent ) const { if( mMouseCaptor && dynamic_cast(mMouseCaptor) != NULL ) { LLView* captor_view = (LLView*)mMouseCaptor; while( captor_view ) { if( captor_view == parent ) { return true; } captor_view = captor_view->getParent(); } } return false; } void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ) { // should be ok to unlock here, as you have to know the locked view // in order to unlock it if (focus == mLockedView) { mLockedView = NULL; } if( mKeyboardFocus == focus ) { mKeyboardFocus = NULL; } } bool LLFocusMgr::keyboardFocusHasAccelerators() const { LLView* focus_view = dynamic_cast(mKeyboardFocus); while( focus_view ) { if(focus_view->hasAccelerators()) { return true; } focus_view = focus_view->getParent(); } return false; } void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor ) { if( new_captor != mMouseCaptor ) { LLMouseHandler* old_captor = mMouseCaptor; mMouseCaptor = new_captor; if (LLView::sDebugMouseHandling) { if (new_captor) { LL_INFOS() << "New mouse captor: " << new_captor->getName() << LL_ENDL; } else { LL_INFOS() << "New mouse captor: NULL" << LL_ENDL; } } if( old_captor ) { old_captor->onMouseCaptureLost(); } } } void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor ) { if( mMouseCaptor == captor ) { mMouseCaptor = NULL; } } bool LLFocusMgr::childIsTopCtrl( const LLView* parent ) const { LLView* top_view = (LLView*)mTopCtrl; while( top_view ) { if( top_view == parent ) { return true; } top_view = top_view->getParent(); } return false; } // set new_top = NULL to release top_view. void LLFocusMgr::setTopCtrl( LLUICtrl* new_top ) { LLUICtrl* old_top = mTopCtrl; if( new_top != old_top ) { mTopCtrl = new_top; if (old_top) { old_top->onTopLost(); } } } void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view ) { if( mTopCtrl == top_view ) { mTopCtrl = NULL; } } void LLFocusMgr::lockFocus() { mLockedView = dynamic_cast(mKeyboardFocus); } void LLFocusMgr::unlockFocus() { mLockedView = NULL; } F32 LLFocusMgr::getFocusFlashAmt() const { return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f); } LLColor4 LLFocusMgr::getFocusColor() const { static LLUIColor focus_color_cached = LLUIColorTable::instance().getColor("FocusColor"); LLColor4 focus_color = lerp(focus_color_cached, LLColor4::white, getFocusFlashAmt()); // de-emphasize keyboard focus when app has lost focus (to avoid typing into wrong window problem) if (!mAppHasFocus) { focus_color.mV[VALPHA] *= 0.4f; } return focus_color; } void LLFocusMgr::triggerFocusFlash() { mFocusFlashTimer.reset(); } void LLFocusMgr::setAppHasFocus(bool focus) { if (!mAppHasFocus && focus) { triggerFocusFlash(); } // release focus from "top ctrl"s, which generally hides them if (!focus) { if(LLUI::instanceExists()) LLUI::getInstance()->clearPopups(); } mAppHasFocus = focus; } LLView* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const { if (subtree_root) { focus_history_map_t::const_iterator found_it = mImpl->mFocusHistory.find(subtree_root->getHandle()); if (found_it != mImpl->mFocusHistory.end()) { // found last focus for this subtree return found_it->second.get(); } } return NULL; } void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root) { if (subtree_root) { mImpl->mFocusHistory.erase(subtree_root->getHandle()); } }