What I wish to do is have a Button display a context menu when right-clicked.
A MouseListener attached to it takes care of displaying the PopupMenu just fine.
The problem is that the Button still receives its “click” event when the right mouse button acts upon it, which is something I don’t want in my use case.
Would it be possible to add an option to the base Button class to avoid that?
EDIT: if it is of any help, this is the patch with which I have hardcoded that behavior (left-click only) in my local JUCE repo. It is provided only to show the sole three lines that would require intervention: left-click-only.txt (1.9 KB)
I’m not sure you understood our use case Jules. here is a example :
you have a large component that contains many children buttons of all kind (textButtons, toggleButtons, etc…)
you want the user to be able to right click anywhere on that parent component to open a contextual menu.
at the moment, if the right click happens over a button, you will get the contextual menu, but the underlying button will also get clicked (and we don’t want that)
so we were more looking for something like a Button::setIgnoreRightClicks() or an additional flag to setInterceptClicks(). for now we have to inherits the buttons class.
I ended up doing something like that :
template <typename ButtonType>
struct ButtonIgnoringRightClick : public ButtonType
{
using ButtonType::ButtonType;
void mouseDown (const MouseEvent& e) override
{
if (e.mods.isPopupMenu())
return;
ButtonType::mouseDown (e);
}
void mouseUp (const MouseEvent& e) override
{
if (e.mods.isPopupMenu())
return;
ButtonType::mouseUp (e);
}
};
What about things like HyperlinkButton that open the link with a right click? Or ToggleButtons that change state with a right click? Neither of those make any sense from a UX perspective. Of course it’s not the end of the world to sub-class, but equally would it really be the end of the world to implement something like Button::setIgnoreRightClicks() or similar?
Unless I missed something about those particular examples that can stop them behaving in that odd manner.
I solved this with a custom class derived by ToggleButton that just ignores the right click:
class LeftOnlyToggleButton : public ToggleButton
{
public:
LeftOnlyToggleButton(String name) : ToggleButton(name)
{
}
// Mouse click action takes place when the mouse button is released
void mouseUp(const MouseEvent& event) override
{
// Ignore if it was a right click
if (event.mods.isPopupMenu())
return;
// Normally (with left click), toggle button state
setToggleState(!getToggleState(), sendNotification);
}
};
And just use LeftOnlyToggleButton as if it was a ToggleButton. You can still addMouseListener(yourListener, false) to catch the right click and maybe open a contextual menu.
This is not really a universal solution because it would require a derived class for every JUCE class that already is derived for Button: we would then end up with LeftOnlyToggleButton, LeftOnlyTextButton, LeftOnlyDrawableButton, LeftOnlyHyperlinkButton, etc.
That’s plain ugly.
@jules, @ed95 : requests in this direction keep appearing: Button right click confusion . Is it so hard to add a setIgnoreRightClicks() in the base Button class?
Also, this solution still has a problem: when the button is right-clicked, it is still animated exactly AS IF a click has been performed on it, while no click has been triggered in reality.
The behaviour that I want to accomplish is right clicks not having any visible action on the Button whatsoever.
That can easily be accomplished by changing 3 lines in juce_Button.cpp where updateState is called, changing them to:
updateState (whatever, isClickButtonDown())
with isClickButtonDown() being a private method like:
bool Button::isClickButtonDown()
{
if (onlyLeftClicks)
return isLeftButtonDown();
return isMouseButtonDown();
}
the 3 places in question are at the beginning of Button::mouseDown(), Button::mouseDrag() and inside the updateState() with no parameters.
Then use it like: LeftClickOnly<ToggleButton> myToggle;
it also works for Sliders etc.
There might be other mouse methods you want to override, but this was sufficient for my needs.
Thank you, that is surprisingly similar to what I devised myself in the mean time , which is:
template <typename ButtonType>
class LeftClick : public ButtonType
{
public:
using ButtonType::ButtonType; // inherit all constructors of the wrapped class
void mouseDown (const MouseEvent& e) override
{
if (e.mods.isPopupMenu())
return;
}
void mouseDrag (const MouseEvent& e) override
{
if (e.mods.isPopupMenu())
return;
}
void mouseUp (const MouseEvent& e) override
{
if (e.mods.isPopupMenu())
return;
}
};
Unfortunately, that suffers of the problem that I have mentioned above: upon right click, the “down” state is painted as if a the mouse action reaches the button, even if a click is not actually triggered.
Upon further investigation, that behaviour is observed only the first time the button is right-clicked after the mouse has entered it.
That happens because in those circumstances the Button receives a call to updateState():
and since isMouseButtonDown() returns true regardless of which mouse button is down, the whole Button enters its “down” state and paints accordingly.
This behaviour cannot be modified because neither Component::isMouseButtonDown() nor Button::updateState() are virtual (the latter also being private).
Yes, and since your paint method only cares about the toggle state of the Button, ignoring its “down” state, you don’t see the effect of the spurious paint that I’m talking about.
@yfede I found that the wrong painting can be avoided by overriding mouseEnter, mouseExit, focusGained, focusLost and internalClickCallback as well. I think this puts me firmly in “evil” land, so if anyone has a cleaner way I’d be happy to know
In focusGained and focusLost you don’t get a MouseEvent, but you can check ModifierKeys::getCurrentModifiers().isPopupMenu() instead.
I’d like a comment from the JUCE Team about whether one really should implement the hacky workarounds proposed so far, or if they could take in consideration my initial feature request which only involves changing few lines in the Button class.
The requested change could also be implemented so that it is effective only when the developer calls a setIgnoreRightClicks() method to be added, so that would be a 100% non-breaking change