In some occasions it is necessary to receive mouseEnter and mouseExit events regardless if the button is pressed or not. So please add the following feature:
Add a flag to Component named similar: receivesMouseEnterExitWhileButtonPressed (bool enter, bool exit). Default should be false to keep current behaviour.
I actually think the best way to implement this would be with the same trick that I’ve been meaning to add for a while, i.e. a new mouse method called mouseCancel(), and a mechanism by which you can steal the mouse from the currently-dragged component. The main motivator for adding this would be for gesture detection to be able to handle things like someone pressing a button, but then spotting that the movement is actually a swipe, and stealing the input from that button without it triggering a mouseUp. In your case, a group of buttons could watch for when the mouse is dragged outside themselves onto another button, and then relinquish the event, allowing the other one to take over.
Is there any chance of something like this being added in the near future? I’m trying to make a row of sliders that can be all set at once by a long mouse drag.
Well never mind I found a solution that seems to work for me. I created a container my sliders are added to which covers them with a component. This cover gets all the mouse events and then checks where to send them based on event position. If component borders are crossed, additional mouseDown and mouseUp calls are created. This works with the Juce Slider class.
/*
==============================================================================
DragDistributionContainer.h
Created: 21 May 2019 9:55:15am
Author: adrianpf
==============================================================================
*/
#pragma once
#include "JuceHeader.h"
class DragDistributionContainer : public Component {
public:
DragDistributionContainer() {
cover.addMouseListener(this, true);
cover.setAlwaysOnTop(true);
addAndMakeVisible(&cover);
}
virtual ~DragDistributionContainer() {}
void resized() override {
cover.setBounds(getLocalBounds());
}
void mouseMove(const MouseEvent& event) override {
if (auto* c = getComponentAtExcludingCover(event.getPosition())) {
c->mouseMove(event.getEventRelativeTo(c));
}
}
void mouseDown(const MouseEvent& event) override {
if (auto* c = getComponentAtExcludingCover(event.getPosition())) {
c->mouseDown(event.getEventRelativeTo(c));
dragComponent = c;
}
}
void mouseDrag(const MouseEvent& event) override {
if (auto* c = getComponentAtExcludingCover(event.getPosition())) {
if (c != dragComponent) {
// switch drag components
if (dragComponent) {
dragComponent->mouseUp(event.getEventRelativeTo(dragComponent));
dragComponent->mouseExit(event.getEventRelativeTo(dragComponent));
}
c->mouseEnter(event.getEventRelativeTo(c));
c->mouseDown(event.getEventRelativeTo(c));
dragComponent = c;
}
}
if (dragComponent) dragComponent->mouseDrag(event.getEventRelativeTo(dragComponent));
}
void mouseUp(const MouseEvent& event) override {
if (auto* c = getComponentAtExcludingCover(event.getPosition())) {
c->mouseUp(event.getEventRelativeTo(c));
}
dragComponent = nullptr;
}
void mouseWheelMove(const MouseEvent& event, const MouseWheelDetails& wheel) override {
if (auto* c = getComponentAtExcludingCover(event.getPosition())) {
c->mouseWheelMove(event.getEventRelativeTo(c), wheel);
}
}
void mouseDoubleClick(const MouseEvent& event) override {
if (auto* c = getComponentAtExcludingCover(event.getPosition())) {
c->mouseDoubleClick(event.getEventRelativeTo(c));
}
}
private:
Component* getComponentAtExcludingCover(Point<int> pt) {
for (auto* c : getChildren()) {
if (c != &cover) {
if (c->getBounds().contains(pt)) {
return c;
}
}
}
return dragComponent;
}
WeakReference<Component> dragComponent;
Component cover;
};
I happy it was so easy to hack something together. This doesn’t account for z-Order and if the components need keyboard events, that would have to be added.
I fixed some issues the initial version had and also used it with Buttons. It only works if the Button is set to toggle on mouseDown using setTriggeredOnMouseDown(). The default Button logic prevents dragging onto the button from working.
Thank you @pflugshaupt for this very useful class!
I just had to add a check for c->isVisible() in getComponentAtExcludingCover() and this works like a charm!
in mouseDrag you could geometrically calc if current drag distance - drag start pos is a point outside of the component’s bounds which means this must be mouseExit behaviour. only problem is this would still not trigger other components’ mouseEnter