ofRegisterMouseEvent on multiple instance

Hi,

I’m trying to make a GUI (Breakpoint function editor) that I can have multiple instance.
I was able to register the mouse event on one instance like this post

But when I create more instance with a vector of shared_ptr, the mouse event are only receive in the first instance ?

I’ve read also this post about the memory addresses that can change in a vector ?

here’s the part of my code:

class UI{
public: 

    ofRectangle hit_zone_;

    UI(){
        ofRegisterMouseEvents(this);
    }

    virtual bool mousePressed(ofMouseEventArgs & args) = 0;

    bool inside(const glm::vec2& hit){
        if(hit_zone_.inside(hit)){
            return true;
        } else
            return false;
    }
};

class Function : public UI{
public:

    Function(){}

    bool mousePressed(ofMouseEventArgs & mouse) override {
        if(hit_zone_.inside(mouse)){
           // do stuff here...
        }
        return true;
    }
};

Then in ofApp.h

 std::vector<std::shared_ptr<UI>> uis;

Then in ofApp.cpp

void ofApp::setup(){
    for(int i=0; i < 4; i++){
         uis.push_back(std::make_shared<Function>());
    }
}
 
void ofApp::mousePressed(int x, int y, int button){
    for(auto &ui : uis){
        if(ui->inside(glm::vec2(x,y)));
    }
}

Hi,
so first of all you need to have all the mouse events callbacks in order to make ofRegisterMouseEvents(this) to work and remember to implement these in the inheriting class.

virtual void mouseDragged (ofMouseEventArgs & args) = 0;
virtual void mouseMoved  (ofMouseEventArgs & args) = 0;
virtual void mousePressed  (ofMouseEventArgs & args) = 0;
virtual void mouseReleased  (ofMouseEventArgs & args) = 0;
virtual void mouseScrolled  (ofMouseEventArgs & args) = 0;
virtual void mouseEntered  (ofMouseEventArgs & args) = 0;
virtual void mouseExited  (ofMouseEventArgs & args) = 0;

Then you are using the callback that returns a bool and you are always returning true. This is used to stop propagation of of event. This means, that when this function returns true, it means that you “used” the event, and no more listeners will be notified. This is very useful in guis, specially when you have overlaping elements, so you only pass the event to the top most one. When you use the mouse callbacks that return void, this does not happen and the event propagation will always continue, thus all the listener will get notified.

so in your Function class the following function should probably look like this

bool mousePressed(ofMouseEventArgs & mouse) override {
        if(hit_zone_.inside(mouse)){
           // do stuff here...
             return true;
        }
        return false;
    }

Also, there is no point on calling

void ofApp::mousePressed(int x, int y, int button){
    for(auto &ui : uis){
        if(ui->inside(glm::vec2(x,y)));
    }
}

as the whole idea of registering the listener class it to avoid having to “manually” pass the mouse event to each object.
Last but not least, I would recommend you to use an ofEventListeners instance, as it will allow you to automatically unregister the events when you destroy any of the UI objects you have created.

listeners.push(ofEvents().mouseDragged.newListener(this, &UI::mouseDragged));
listeners.push(ofEvents().mouseMoved.newListener(this, &UI::mouseMoved));
listeners.push(ofEvents().mousePressed.newListener(this, &UI::mousePressed));
listeners.push(ofEvents().mouseReleased.newListener(this, &UI::mouseReleased));
listeners.push(ofEvents().mouseScrolled.newListener(this, &UI::mouseScrolled));
listeners.push(ofEvents().mouseEntered.newListener(this, &UI::mouseEntered));
listeners.push(ofEvents().mouseExited.newListener(this, &UI::mouseExited));

your UI class would look something like


class UI{
public: 

    ofRectangle hit_zone_;

    UI(){
		listeners.push(ofEvents().mouseDragged.newListener(this, &UI::mouseDragged));
		listeners.push(ofEvents().mouseMoved.newListener(this, &UI::mouseMoved));
		listeners.push(ofEvents().mousePressed.newListener(this, &UI::mousePressed));
		listeners.push(ofEvents().mouseReleased.newListener(this, &UI::mouseReleased));
		listeners.push(ofEvents().mouseScrolled.newListener(this, &UI::mouseScrolled));
		listeners.push(ofEvents().mouseEntered.newListener(this, &UI::mouseEntered));
		listeners.push(ofEvents().mouseExited.newListener(this, &UI::mouseExited));
    }

	virtual void mouseDragged (ofMouseEventArgs & args) = 0;
	virtual void mouseMoved  (ofMouseEventArgs & args) = 0;
	virtual void mousePressed  (ofMouseEventArgs & args) = 0;
	virtual void mouseReleased  (ofMouseEventArgs & args) = 0;
	virtual void mouseScrolled  (ofMouseEventArgs & args) = 0;
	virtual void mouseEntered  (ofMouseEventArgs & args) = 0;
	virtual void mouseExited  (ofMouseEventArgs & args) = 0;


    bool inside(const glm::vec2& hit){
        if(hit_zone_.inside(hit)){
            return true;
        } else
            return false;
    }

    ofEventListeners listeners;
};

Thank’s roy ! very helpfull…
I just remove mouse events callbacks from my post for lightness, but thanks to mention anyway.
The problem was effectively my ‘mousePressed’ callback that always returning true.

And for the ofEventListeners, I thought that the shared_ptr that I use to create the object clear the memory after deleting it… but if I understand, it’s just a question of ‘unregister’ the listener.

correct. but, it also need to remove the “registered” listener, which is being stored elsewhere. If you dont unregister, you could run into problems when desalocating as the listeners will have pointer to a null pointer.

1 Like