CHOP-763: Add LLView::TemporaryDrilldownFunc to support UI injection.

Instead of unconditionally calling LLView::pointInView(),
LLView::visibleAndContains() now consults a class-static boost::function
called sDrilldown -- which is initialized to LLView::pointInView().
Introduce LLView::TemporaryDrilldownFunc, instantiated with a callable whose
signature is compatible with LLView::pointInView(). This replaces sDrilldown,
but only for the life of the TemporaryDrilldownFunc object.
Introduce llview::TargetEvent, an object intended to serve as a
TemporaryDrilldownFunc callable. Construct it with a desired target LLView*
and pass it to TemporaryDrilldownFunc. When called with each candidate child
LLView*, instead of selecting the one containing the particular (x, y) point,
it selects the one that will lead to the ultimate desired target LLView*.
Add optional 'recur' param to LLView::childFromPoint(); default is current
one-level behavior. But when you pass recur=true, it should return the
frontmost visible leaf LLView containing the passed (x, y) point.
master
Nat Goodspeed 2011-08-25 14:40:53 -04:00
parent a548fd52e3
commit f7a6ed85e4
5 changed files with 152 additions and 4 deletions

View File

@ -111,6 +111,7 @@ set(llui_SOURCE_FILES
llurlmatch.cpp
llurlregistry.cpp
llviewborder.cpp
llviewinject.cpp
llviewmodel.cpp
llview.cpp
llviewquery.cpp
@ -214,6 +215,7 @@ set(llui_HEADER_FILES
llurlmatch.h
llurlregistry.h
llviewborder.h
llviewinject.h
llviewmodel.h
llview.h
llviewquery.h

View File

@ -33,6 +33,7 @@
#include <cassert>
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include "llrender.h"
#include "llevent.h"
@ -67,6 +68,8 @@ S32 LLView::sLastLeftXML = S32_MIN;
S32 LLView::sLastBottomXML = S32_MIN;
std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack;
LLView::DrilldownFunc LLView::sDrilldown =
boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT);
//#if LL_DEBUG
BOOL LLView::sIsDrawing = FALSE;
@ -645,7 +648,7 @@ void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
bool LLView::visibleAndContains(S32 local_x, S32 local_y)
{
return pointInView(local_x, local_y)
return sDrilldown(this, local_x, local_y)
&& getVisible();
}
@ -789,10 +792,11 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
return NULL;
}
LLView* LLView::childFromPoint(S32 x, S32 y)
LLView* LLView::childFromPoint(S32 x, S32 y, bool recur)
{
if (!getVisible() )
if (!getVisible())
return false;
BOOST_FOREACH(LLView* viewp, mChildList)
{
S32 local_x = x - viewp->getRect().mLeft;
@ -801,6 +805,14 @@ LLView* LLView::childFromPoint(S32 x, S32 y)
{
continue;
}
// Here we've found the first (frontmost) visible child at this level
// containing the specified point. Is the caller asking us to drill
// down and return the innermost leaf child at this point, or just the
// top-level child?
if (recur)
{
return viewp->childFromPoint(local_x, local_y, recur);
}
return viewp;
}

View File

@ -50,6 +50,7 @@
#include "llfocusmgr.h"
#include <list>
#include <boost/function.hpp>
class LLSD;
@ -437,7 +438,7 @@ public:
/*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
/*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
virtual LLView* childFromPoint(S32 x, S32 y);
virtual LLView* childFromPoint(S32 x, S32 y, bool recur=false);
// view-specific handlers
virtual void onMouseEnter(S32 x, S32 y, MASK mask);
@ -599,7 +600,35 @@ private:
LLView& getDefaultWidgetContainer() const;
// This allows special mouse-event targeting logic for testing.
typedef boost::function<bool(const LLView*, S32 x, S32 y)> DrilldownFunc;
static DrilldownFunc sDrilldown;
public:
// This is the only public accessor to alter sDrilldown. This is not
// an accident. The intended usage pattern is like:
// {
// LLView::TemporaryDrilldownFunc scoped_func(myfunctor);
// // ... test with myfunctor ...
// } // exiting block restores original LLView::sDrilldown
class TemporaryDrilldownFunc
{
public:
TemporaryDrilldownFunc(const DrilldownFunc& func):
mOldDrilldown(sDrilldown)
{
sDrilldown = func;
}
~TemporaryDrilldownFunc()
{
sDrilldown = mOldDrilldown;
}
private:
DrilldownFunc mOldDrilldown;
};
// Depth in view hierarchy during rendering
static S32 sDepth;

View File

@ -0,0 +1,49 @@
/**
* @file llviewinject.cpp
* @author Nat Goodspeed
* @date 2011-08-16
* @brief Implementation for llviewinject.
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Copyright (c) 2011, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llviewinject.h"
// STL headers
// std headers
// external library headers
// other Linden headers
llview::TargetEvent::TargetEvent(LLView* view)
{
// Walk up the view tree from target LLView to the root (NULL). If
// passed NULL, iterate 0 times.
for (; view; view = view->getParent())
{
// At each level, operator() is going to ask: for a particular parent
// LLView*, which of its children should I select? So for this view's
// parent, select this view.
mChildMap[view->getParent()] = view;
}
}
bool llview::TargetEvent::operator()(const LLView* view, S32 /*x*/, S32 /*y*/) const
{
// We are being called to decide whether to direct an incoming mouse event
// to this child view. (Normal LLView processing is to check whether the
// incoming (x, y) is within the view.) Look up the parent to decide
// whether, for that parent, this is the previously-selected child.
ChildMap::const_iterator found(mChildMap.find(view->getParent()));
// If we're looking at a child whose parent isn't even in the map, never
// mind.
if (found == mChildMap.end())
{
return false;
}
// So, is this the predestined child for this parent?
return (view == found->second);
}

56
indra/llui/llviewinject.h Normal file
View File

@ -0,0 +1,56 @@
/**
* @file llviewinject.h
* @author Nat Goodspeed
* @date 2011-08-16
* @brief Supplemental LLView functionality used for simulating UI events.
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Copyright (c) 2011, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_LLVIEWINJECT_H)
#define LL_LLVIEWINJECT_H
#include "llview.h"
#include <map>
namespace llview
{
/**
* TargetEvent is a callable with state, specifically intended for use as
* an LLView::TemporaryDrilldownFunc. Instantiate it with the desired
* target LLView*; pass it to a TemporaryDrilldownFunc instance;
* TargetEvent::operator() will then attempt to direct subsequent mouse
* events to the desired target LLView*. (This is an "attempt" because
* LLView will still balk unless the target LLView and every parent are
* visible and enabled.)
*/
class TargetEvent
{
public:
/**
* Construct TargetEvent with the desired target LLView*. (See
* LLUI::resolvePath() to obtain an LLView* given a string pathname.)
* This sets up for operator().
*/
TargetEvent(LLView* view);
/**
* This signature must match LLView::DrilldownFunc. When you install
* this TargetEvent instance using LLView::TemporaryDrilldownFunc,
* LLView will call this method to decide whether to propagate an
* incoming mouse event to the passed child LLView*.
*/
bool operator()(const LLView*, S32 x, S32 y) const;
private:
// For a given parent LLView, identify which child to select.
typedef std::map<LLView*, LLView*> ChildMap;
ChildMap mChildMap;
};
} // llview namespace
#endif /* ! defined(LL_LLVIEWINJECT_H) */