Adding mouse events to our interactive object

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.

Getting ready

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.

How to do it…

We will make our InteractiveObject class and send its own events whenever it interacts with the cursor.

  1. Let's create a class to use as an argument when sending events. Add the following code in the file 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;
    };
  2. In the 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;
  3. Now we will need to add a method so that other objects can register to receive events. Since the method will use a template, we will declare and implement it in the 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 ) );
    }
  4. Let's also create a method so that objects can unregister from receiving further events. Declare the following method:
    void removeListener( ci::CallbackId callId );
  5. Let's implement the removeListener method. Add the following code in the InteractiveObject.cpp file:
    void InteractiveObject::removeListener( CallbackId callbackId ){
      mEvents.unregisterCb( callbackId );
    }
  6. Modify the methods 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 ) );
      }
     }
    }
  7. With our 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 );
  8. Let's implement the 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;
    }
  9. All that is left is to register for the events. In the setup method add the following code after mObject has been initialized:
    mObject->addListener( this, &InteractiveObjectApp::receivedEvent );
  10. Now build and run the application and use the mouse to interact with the rectangle on the window. Whenever a mouse event occurs on mObject, our method, receivedEvent, will be called.

How it works…

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.

There's more…

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:

  1. Include the InteractiveObject.h and cinder/gl/texture.h files:
    #include "InteractiveObject.h"
    #include "cinder/gl/Texture.h"
  2. Declare the following class:
    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;
    };
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.71.106