/** @return one event has been posted
*/ template <typename ContainerT> bool fireSingleEvent( ContainerT & rQueue, EventQueue & rEventQueue )
{ // post next event in given queue: while (! rQueue.empty())
{
EventSharedPtr const pEvent(rQueue.front());
rQueue.pop();
// skip all inactive events (as the purpose of // nextEventFromQueue() is to activate the next // event, and events which return false on // isCharged() will never be activated by the // EventQueue) if(pEvent->isCharged()) return rEventQueue.addEvent( pEvent );
} returnfalse; // no more (active) events in queue
}
/** @return at least one event has been posted
*/ template <typename ContainerT> bool fireAllEvents( ContainerT & rQueue, EventQueue & rEventQueue )
{ bool bFiredAny = false; while (fireSingleEvent( rQueue, rEventQueue ))
bFiredAny = true; return bFiredAny;
}
class EventContainer
{ public:
EventContainer() :
maEvents()
{}
/** Remember to trigger (or not to trigger) the next effect after the currenteffectisskipped.
*/ void setSkipTriggersNextEffect (constbool bSkipTriggersNextEffect)
{ mbSkipTriggersNextEffect = bSkipTriggersNextEffect; }
/// Skip the current effect but do not trigger the next effect. void skipEffect() { handleEvent_impl(false); }
bool handleEvent_impl (bool bNotifyNextEffect)
{ // fire all events, so animation nodes can register their // next effect listeners: if(fireAllEvents( maEvents, mrEventQueue ))
{ if (mbSkipTriggersNextEffect && bNotifyNextEffect)
{ // then simulate a next effect event: this skip effect // handler is triggered upon next effect events (multiplexer // prio=-1)! Posting a notifyNextEffect() here is only safe // (we don't run into busy loop), because we assume that // someone has registered above for next effects // (multiplexer prio=0) at the user event queue. return mrEventQueue.addEventWhenQueueIsEmpty(
makeEvent( [this] () { this->mrEventMultiplexer.notifyNextEffect();
}, u"EventMultiplexer::notifyNextEffect"_ustr) );
} else returntrue;
} returnfalse;
}
void addEvent( const EventSharedPtr& rEvent, const ShapeSharedPtr& rShape )
{
ImpShapeEventMap::iterator aIter; if( (aIter=maShapeEventMap.find( rShape )) == maShapeEventMap.end() )
{ // no entry for this shape -> create one
aIter = maShapeEventMap.emplace(rShape, ImpEventQueue()).first;
}
// add new event to queue
aIter->second.push( rEvent );
}
protected: bool hitTest( const awt::MouseEvent& e,
ImpShapeEventMap::reverse_iterator& o_rHitShape )
{ // find hit shape in map const basegfx::B2DPoint aPosition( e.X, e.Y );
// find matching shape (scan reversely, to coarsely match // paint order) auto aCurrShape = std::find_if(maShapeEventMap.rbegin(), maShapeEventMap.rend(),
[&aPosition](const ImpShapeEventMap::value_type& rShape) { // TODO(F2): Get proper geometry polygon from the // shape, to avoid having areas outside the shape // react on the mouse return rShape.first->getBounds().isInside( aPosition )
&& rShape.first->isVisible();
}); if (aCurrShape != maShapeEventMap.rend())
{ // shape hit, and shape is visible - report a // hit
o_rHitShape = aCurrShape; returntrue;
}
returnfalse; // nothing hit
}
bool sendEvent( ImpShapeEventMap::reverse_iterator const & io_rHitShape )
{ // take next event from queue constbool bRet( fireSingleEvent( io_rHitShape->second,
mrEventQueue ) );
// clear shape entry, if its queue is // empty. This is important, since the shapes // are held by shared ptr, and might otherwise // not get released, even after their owning // slide is long gone. if( io_rHitShape->second.empty() )
{ // this looks funny, since ::std::map does // provide an erase( iterator ) // method. Unfortunately, C++ does not // declare the obvious erase( // reverse_iterator ) needed here (missing // orthogonality, eh?)
maShapeEventMap.erase( io_rHitShape->first );
}
return bRet;
}
bool processEvent( const awt::MouseEvent& e )
{
ImpShapeEventMap::reverse_iterator aCurrShape;
if( hitTest( e, aCurrShape ) ) return sendEvent( aCurrShape );
virtualbool handleMouseReleased( const awt::MouseEvent& e ) override
{ if(e.Buttons != awt::MouseButton::LEFT) returnfalse; return processEvent( e );
}
virtualbool handleMouseMoved( const awt::MouseEvent& e ) override
{ // TODO(P2): Maybe buffer last shape touched
// if we have a shape click event, and the mouse // hovers over this shape, change cursor to hand
ImpShapeEventMap::reverse_iterator aDummy; if( hitTest( e, aDummy ) )
mrCursorManager.requestCursor( awt::SystemPointer::REFHAND );
returnfalse; // we don't /eat/ this event. Lower prio // handler should see it, too.
}
private:
CursorManager& mrCursorManager;
};
class MouseEnterHandler : public MouseHandlerBase
{ public: explicit MouseEnterHandler( EventQueue& rEventQueue )
: MouseHandlerBase( rEventQueue ),
mpLastShape() {}
virtualbool handleMouseMoved( const awt::MouseEvent& e ) override
{ // TODO(P2): Maybe buffer last shape touched, and // check against that _first_
ImpShapeEventMap::reverse_iterator aCurr; if( hitTest( e, aCurr ) )
{ if( aCurr->first != mpLastShape )
{ // we actually hit a shape, and it's different // from the previous one - thus we just // entered it, raise event
sendEvent( aCurr );
mpLastShape = aCurr->first;
}
} else
{ // don't hit no shape - thus, last shape is NULL
mpLastShape.reset();
}
returnfalse; // we don't /eat/ this event. Lower prio // handler should see it, too.
}
private:
ShapeSharedPtr mpLastShape;
};
class MouseLeaveHandler : public MouseHandlerBase
{ public: explicit MouseLeaveHandler( EventQueue& rEventQueue )
: MouseHandlerBase( rEventQueue ),
maLastIter() {}
virtualbool handleMouseMoved( const awt::MouseEvent& e ) override
{ // TODO(P2): Maybe buffer last shape touched, and // check against that _first_
ImpShapeEventMap::reverse_iterator aCurr; if( hitTest( e, aCurr ) )
{
maLastIter = aCurr;
} else
{ if( maLastIter->first )
{ // last time, we were over a shape, now we're // not - we thus just left that shape, raise // event
sendEvent( maLastIter );
}
// in any case, when we hit this else-branch: no // shape hit, thus have to clear maLastIter
maLastIter = ImpShapeEventMap::reverse_iterator();
}
returnfalse; // we don't /eat/ this event. Lower prio // handler should see it, too.
}
// forward to handler, if existing. Otherwise, the handler // creation will do the forwarding. if( mpClickEventHandler )
mpClickEventHandler->setAdvanceOnClick( bAdvanceOnClick );
}
voidoperator()( const std::shared_ptr<ClickEventHandler>& rHandler )const
{ // register the handler on _two_ sources: we want the // nextEffect events, e.g. space bar, to trigger clicks, as well!
mrMultiplexer.addClickHandler( rHandler, mnPrio );
mrMultiplexer.addNextEffectHandler( rHandler, mnPrio );
// forward advance-on-click state to newly // generated handler (that's the only reason why // we're called here)
rHandler->setAdvanceOnClick( mbAdvanceOnClick );
}
void UserEventQueue::registerNextEffectEvent( const EventSharedPtr& rEvent )
{ // TODO: better name may be mpNextEffectEventHandler? then we have // next effect (=> waiting to be started) // skip effect (skipping the currently running one) // rewind effect (rewinding back running one and waiting (again) // to be started)
registerEvent( mpClickEventHandler,
rEvent,
ClickEventRegistrationFunctor( mrMultiplexer, 0.0/* default prio */,
mbAdvanceOnClick ) );
}
void UserEventQueue::registerSkipEffectEvent(
EventSharedPtr const & pEvent, constbool bSkipTriggersNextEffect)
{ if(!mpSkipEffectEventHandler)
{
mpSkipEffectEventHandler =
std::make_shared<SkipEffectEventHandler>( mrEventQueue, mrMultiplexer ); // register the handler on _two_ sources: we want the // nextEffect events, e.g. space bar, to trigger clicks, as well!
mrMultiplexer.addClickHandler( mpSkipEffectEventHandler,
-1.0/* prio below default */ );
mrMultiplexer.addNextEffectHandler( mpSkipEffectEventHandler,
-1.0/* prio below default */ ); // forward advance-on-click state to newly // generated handler (that's the only reason why // we're called here)
mpSkipEffectEventHandler->setAdvanceOnClick( mbAdvanceOnClick );
}
mpSkipEffectEventHandler->setSkipTriggersNextEffect(bSkipTriggersNextEffect);
mpSkipEffectEventHandler->addEvent( pEvent );
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.