Confusing mouse click and mouse wheel event propagation

Hi folks

If you run the following code:

(
~childViews = List();
~addChildView = {
	|child|
	~childViews.add(Dictionary.newFrom([\view, child, \originalBounds, child.bounds]););
};
~resizeChildren = {
	|scale|
	~childViews.do({
		|child|
		child[\view].bounds = Rect(child[\originalBounds].left*scale,child[\originalBounds].top*scale,child[\originalBounds].width*scale,child[\originalBounds].height*scale);
	});
};

~window = Window("Zooming and scrolling", Rect(50,50,800,600));
~scale = 1.0;
~originalHeight = 100;
~originalWidth = 100;
~zoomme = View(~window, Rect(50, 50, ~originalWidth, ~originalHeight)).background_(Color.black)
.mouseWheelAction_({
	|view, x, y, modifiers, xDelta, yDelta|
	//[view, x, y, modifiers, xDelta, yDelta].postln;
	~scale = ~scale * 1 + (yDelta*0.005);
	postln(format("scale: %", ~scale));
	~zoomme.resizeTo(~originalWidth * ~scale, ~originalHeight * ~scale);
	~resizeChildren.value(~scale);
}).mouseDownAction_({postln("I'm black!")});
~addChildView.value(View(~zoomme,Rect(10,40,20,20)).background_(Color.red).mouseDownAction_(postln("I'm red!")));
~addChildView.value(View(~zoomme,Rect(40,20,20,20)).background_(Color.blue).mouseDownAction_(postln("I'm blue!")));
~addChildView.value(View(~zoomme,Rect(70,40,20,20)).background_(Color.green).mouseDownAction_(postln("I'm green!")));

~window.front;
)

… you will see that if you click on one of the coloured boxes, the event is handled by the parent black box, but if you mouse wheel over one of the coloured boxes, that event is not handled by the parent black box. This is in fact the exact opposite of what I want. :slight_smile:

How do I get the click events to be handled by the coloured boxes, while any mouse wheel event is always handled by the parent black box?

Okay, so the reason the coloured boxes aren’t handling clicks is because I set the handler wrong:

.mouseDownAction_(postln("I'm red!"))

… should be …

.mouseDownAction_({postln("I'm red!")})

… or, to prevent the parent black box also handling the click …

.mouseDownAction_({postln("I'm red!");true;})

Yet to figure out how to stop the coloured boxes taking the mouse wheel actions from the parent black box.

Calling the parent’s mouseWheelAction handler from the child’s does work:

child.mouseWheelAction_({
	|view, x, y, modifiers, xDelta, yDelta|
	child.parent.mouseWheelAction.value(view, x, y, modifiers, xDelta, yDelta);
});

Seems clunky though; wondering if there’s something I’ve missed.

Is this the outcome you intended to achieve?

(
~childViews = List();

~addChildView = { |child|
	~childViews.add((view: child, originalBounds: child.bounds));
};

~resizeChildren = { |scale|
	~childViews.do { |child|
		var bounds = child[\originalBounds];
		child[\view].bounds = (bounds.asArray * scale).asRect
	};
};

~window = Window("Zooming and scrolling", Rect(50,50,800,600));

~scale = 1.0;
~originalHeight = 100;
~originalWidth = 100;

~zoomme = View(~window, Rect(50, 50, ~originalWidth, ~originalHeight))
.background_(Color.black)
.mouseWheelAction_ { |view, x, y, modifiers, xDelta, yDelta|
	~scale = ~scale + (yDelta * 0.005);
	postln("scale: " ++ ~scale);
	~zoomme.resizeTo(~originalWidth * ~scale, ~originalHeight * ~scale);
	~resizeChildren.value(~scale);
}
.mouseDownAction_{ |view, x, y, modifiers|
	var clickedInsideChild = ~childViews.any { |child|
		child[\view].bounds.containsPoint(Point(x, y))
	};
	if(clickedInsideChild.not) {
		postln("I'm black!");
	};
};

~makeChild = { |parent, rect, color, message|
	var v = View(parent, rect)
		.background_(color)
	.mouseDownAction_{ postf("I'm %!\n", message) };
	~addChildView.value(v);
};

~makeChild.(~zoomme, Rect(10,40,20,20), Color.red, "red");
~makeChild.(~zoomme, Rect(40,20,20,20), Color.blue, "blue");
~makeChild.(~zoomme, Rect(70,40,20,20), Color.green, "green");

~window.front;
)

The mouseWheelAction_ and mouseDownAction_ methods exhibit different behaviour when interacting with a child view:

  • mouseWheelAction_ ceases its action.
  • mouseDownAction_ continues its action.

It is unclear whether this difference in behaviour is intentional, an unintended but acceptable design choice, or a genuine bug.

Hi @prko. Thanks for looking at this. It’s not quite what I was looking for, since I want the user to be able to resize the whole set of views with the scroll wheel regardless of whether the pointer is over a child view or not. I found the simplest thing to do was for the child views to handle the mouse wheel event by calling the parent’s mouse wheel event.

With the click action, I think it is simpler to cancel the parent’s click action in the child’s click action by returning true.

Ah, sorry! I only read the code without reading your text…

Maybe then this?

(
~childViews = List();

~addChildView = { |child|
	~childViews.add((view: child, originalBounds: child.bounds));
};

~resizeChildren = { |scale|
	~childViews.do { |child|
		var bounds = child[\originalBounds];
		child[\view].bounds = (bounds.asArray * scale).asRect;
	};
};

~window = Window("Zooming and scrolling", Rect(50,50,800,600));

~scale = 1.0;
~originalHeight = 100;
~originalWidth = 100;

~zoomme = View(~window, Rect(50, 50, ~originalWidth, ~originalHeight))
.background_(Color.black)
.mouseWheelAction_ { |view, x, y, modifiers, xDelta, yDelta|
	~scale = ~scale + (yDelta * 0.005);
	postln("scale: " ++ ~scale);
	~zoomme.resizeTo(~originalWidth * ~scale, ~originalHeight * ~scale);
	~resizeChildren.(~scale);
}
.mouseDownAction_{ |view, x, y, modifiers|
		postln("I'm black!");
};

~makeChild = { |parent, rect, color, message|
	var v = View(parent, rect)
	.background_(color)
	.mouseWheelAction_{ |view, x, y, modifiers, xDelta, yDelta|
		parent.mouseWheelAction.(view, x, y, modifiers, xDelta, yDelta); // call parent's mouseWheelAction.
	}
	.mouseDownAction_{ |view, x, y, modifiers|
		postf("I'm %!\n", message);
		true; // stop event propagation to parent.
	};
	~addChildView.(v);
};

~makeChild.(~zoomme, Rect(10,40,20,20), Color.red, "red");
~makeChild.(~zoomme, Rect(40,20,20,20), Color.blue, "blue");
~makeChild.(~zoomme, Rect(70,40,20,20), Color.green, "green");

~window.front;
)

With the click action, I think it is simpler to cancel the parent’s click action in the child’s click action by returning true .

Yes, indeed! I forgot the event propagation from child to parent!

Yes! Exactly that! Now the message is more than 20 characters long.