In this recipe, we will continue with the previous recipe, Creating an interactive object that responds to the mouse and add the mouse events to our InteractiveObject
class so that other objects can register and receive notifications whenever a mouse event occurs.
Grab the code from the recipe Creating an interactive object that responds to the mouse and add it to your project, as we will continue on from what was made earlier.
We will make our InteractiveObject
class and send its own events whenever it interacts with the cursor.
InteractiveObject.h
right before the InteractiveObject
class declaration:class InteractiveObject; class InteractiveObjectEvent: public ci::app::Event{ public: enum EventType{ Pressed, PressedOutside, Released, ReleasedOutside, RolledOut, RolledOver, Dragged }; InteractiveObjectEvent( InteractiveObject *sender, EventType type ){ this->sender = sender; this->type = type; } InteractiveObject *sender; EventType type; };
InteractiveObject
class, we will need to declare a member to manage the registered objects using the ci::CallbakcMgr
class. Declare the following code as a protected member:ci::CallbackMgr< void(InteractiveObjectEvent) > mEvents;
InteraciveObject.h
file. Add the following member method:template< class T > ci::CallbackId addListener( T* listener, void (T::*callback)(InteractiveObjectEvent) ){ return mEvents.registerCb( std::bind1st( std::mem_fun( callback ), listener ) ); }
void removeListener( ci::CallbackId callId );
removeListener
method. Add the following code in the InteractiveObject.cpp
file:void InteractiveObject::removeListener( CallbackId callbackId ){ mEvents.unregisterCb( callbackId ); }
mouseDown
, mouseUp
, mouseDrag
, and mouseMove
so that mEvents
gets called whenever an event occurs. The implementation of these methods should be as follows:void InteractiveObject::mouseDown( MouseEvent& event ){ if( rect.contains( event.getPos() ) ){ mPressed = true; mOver = false; pressed(); mEvents.call( InteractiveObjectEvent( this, InteractiveObjectEvent::Pressed ) ); } else { pressedOutside(); mEvents.call( InteractiveObjectEvent( this, InteractiveObjectEvent::PressedOutside ) ); } } void InteractiveObject::mouseUp( MouseEvent& event ){ if( rect.contains( event.getPos() ) ){ if( mPressed ){ mPressed = false; mOver = true; released(); mEvents.call( InteractiveObjectEvent( this, InteractiveObjectEvent::Released ) ); } } else { mPressed = false; mOver = false; releasedOutside(); mEvents.call( InteractiveObjectEvent( this, InteractiveObjectEvent::ReleasedOutside ) ); } } void InteractiveObject::mouseDrag( MouseEvent& event ){ if( mPressed && rect.contains( event.getPos() ) ){ mPressed = true; mOver = false; dragged(); mEvents.call( InteractiveObjectEvent( this, InteractiveObjectEvent::Dragged ) ); } } void InteractiveObject::mouseMove( MouseEvent& event ){ if( rect.contains( event.getPos() ) ){ if( mOver == false ){ mPressed = false; mOver = true; rolledOver(); mEvents.call( InteractiveObjectEvent( this, InteractiveObjectEvent::RolledOver ) ); } } else { if( mOver ){ mPressed = false; mOver = false; rolledOut(); mEvents.call( InteractiveObjectEvent( this, InteractiveObjectEvent::RolledOut ) ); } } }
InteractiveObject
class ready, we need to register our application class to receive its events. In your application class declaration add the following method:void receivedEvent( InteractiveObjectEvent event );
receivedEvent
method. We will check what type of event has been received and print a message to the console.void MyApp::receivedEvent( InteractiveObjectEvent event ){ string text; switch( event.type ){ case InteractiveObjectEvent::Pressed: text = "Pressed event"; break; case InteractiveObjectEvent::PressedOutside: text = "Pressed outside event"; break; case InteractiveObjectEvent::Released: text = "Released event"; break; case InteractiveObjectEvent::ReleasedOutside: text = "Released outside event"; break; case InteractiveObjectEvent::RolledOver: text = "RolledOver event"; break; case InteractiveObjectEvent::RolledOut: text = "RolledOut event"; break; case InteractiveObjectEvent::Dragged: text = "Dragged event"; break; default: text = "Unknown event"; } console() << "Received " + text << endl; }
setup
method add the following code after mObject
has been initialized:mObject->addListener( this, &InteractiveObjectApp::receivedEvent );
mObject
, our method, receivedEvent
, will be called.We are using the template class ci::CallbakMgr
to manage our event listeners. This class takes a template with the signature of the methods that can be registered. In our previous code, we declared mEvents
to be of type ci::CallbakcMgr<void(InteractiveObjectEvent)>;
it means that only methods that return void
and receive InteractiveObejctEvent
as a parameter can be registered.
The template method registerEvent
will take an object pointer and method pointer. These are bound to std::function
using std::bind1st
and added to mEvents
. The method will return ci::CallbackId
with the identification of the listener. The ci::CallbackId
can be used to unregister listeners.
The InteractiveObject
class is very useful for creating user interfaces. If we want to create a Button
class using three textures (for displaying when pressed, over, and idle), we can do so as follows:
InteractiveObject.h
and cinder/gl/texture.h
files:#include "InteractiveObject.h" #include "cinder/gl/Texture.h"
class Button: public InteractiveObject{ public: Button( const ci::Rectf& rect, ci::gl::Texture idleTex, ci::gl::Texture overTex, ci::gl::Texture pressTex) :InteractiveObject( rect ) { mIdleTex = idleTex; mOverTex = overTex; mPressTex = pressTex; } virtual void draw(){ if( mPressed ){ ci::gl::draw( mPressTex, rect ); } else if( mOver ){ ci::gl::draw( mOverTex, rect ); } else { ci::gl::draw( mPressTex, rect ); } } protected: ci::gl::Texture mIdleTex, mOverTex, mPressTex; };
3.22.71.106