How to keep parent from getting mouse buttons?

I’m trying to make a “child window” pop up on top of a “parent window”.

When I do this, it all draws fine, but the event propegation is out of control.

If I click a mouse button on the child, the parent’s onButton is still called.

How can I make a child widget such that mouse clicks on the child will not be sent to the parent?

class ParentWindow : public OpaqueWidget
{
public:
    void draw(const DrawArgs &args) override
    {
        NVGcontext *vg = args.vg;
        NVGcolor color     = nvgRGB(0x80, 0x80, 0x80);
        SqGfx::filledRect(vg, color, 0, 0, box.size.x, box.size.y);
        Widget::draw(args);
    }

    void onButton(const event::Button &e) override
    {
        DEBUG("Parent window on button consumed=%d", e.isConsumed());
        OpaqueWidget::onButton(e);      // If I don't do this, child doesn't get buttons
        DEBUG("Parent: after call base, cons=%d", e.isConsumed());
    }
};

class ChildWindow : public OpaqueWidget
{
public:
    void draw(const DrawArgs &args) override
    {
        NVGcontext *vg = args.vg;
        NVGcolor color     = nvgRGB(0xf0, 0x80, 0x80);
        SqGfx::filledRect(vg, color, 0, 0, box.size.x, box.size.y);
        Widget::draw(args);
    }

    void onButton(const event::Button &e) override
    {
        DEBUG("Child window on button");
        OpaqueWidget::onButton(e);     
    }
};

BlankWidget::BlankWidget(BlankModule *module)
{
    setModule(module);
    box.size = Vec(6 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
    SqHelper::setPanel(this, "res/blank_panel.svg");

    auto winp = new ParentWindow();
    winp->box.pos = Vec(40, 40);
    winp->box.size = Vec(100, 100);
    addChild(winp);

    auto win = new ChildWindow();
    win->box.pos = Vec(40, 40);
    win->box.size = Vec(50, 50);
    winp->addChild(win);
}

The Button event is not “sent to the parent”. It begins with the parent, which recursively calls the child’s Button handler. It is impossible to prevent the parent’s onButton from being called while allowing the child’s onButton to be called.

The parent window onButton can call the base method FIRST; and then check to see if the event has been consumed by a child. Then the parent can do its own behaviour if the event has not already been consumed.

ah, good idea!

Yes, this is a common pattern and is used by OpaqueWidget itself.

	void onButton(const event::Button& e) override {
		Widget::onButton(e);
		if (e.isConsumed())
			return;
		if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
			e.consume(this);
			// Do stuff
			// ...
		}
	}
1 Like