What about ComponentDraggerListener?


#1

i have a parent component, which have a lot of child components each one of them can be dragged around and thus have its own member ComponentDragger that it’s called in its mouseDown and mouseDrag.

now in my parent component i take care of the selection and deselection of objects (with mouse clicks, keypresses and LassoComponent), and i want to move all the selected childs when i drag a single one.

I think about a ComponentDraggerListener interface, that i will register in my own parent component, so that it gets triggered when a single component is dragged and i can take care of moving the other ones i have selected, cause it will have only a function that takes the new relative position of the component:

class ComponentDraggerListener
{
public:

    virtual ~ComponentDraggerListener () {}

    virtual void componentDragged (Component* const componentDragged, const int draggedByDeltaX, const int draggedByDeltaY) = 0;
};

or i should use only a single ComponentDragger inside the parent component, and call that passing a different pointer when a child component is dragged ?

or i should throw in a bunch or ComponentMovementWatcher ?

what do you think ?
which is the best approach ?


#2

TBH I’d suggest that this sort of stuff shouldn’t be done in the components at all, but should be something that your data model takes care of - i.e. you tell all the selected objects to move, and then their corresponding UI components all receive change messages from the objects and update their own positions. That’s the sort of approach the Jucer uses to drag multiple objects.


#3

ok, my data model is not more than a list of audio plugins, and i don’t want to keep gui stuff tight to my plugins classes.

when i should tell my objects to move ? Apart from the Component::moved callback i don’t have any other way to intercept component dragging done by the ComponentDragger and notify my model to move my objects.
After all, the move is triggered by a user interaction from a mouse drag over a component: i can handle this internally in the mouseDrag of the component but i will end duplicating some of the code of the ComponentDragger itself.

I’ll try looking at the jucer code and try to understand how you have done, but from what i see it is a bit different from my logic.


#4

I think in the jucer I didn’t actually use a componentdragger. The neatest way is to catch the mousedrags, but rather than using them to directly move the components, just call your data model and change the position. Then your components should all be registered as listeners with your model, and should move themselves accordingly.


#5

I’m facing the same kind of issue, and I was thinking of an approach but I’d like some advice from Julian and the other experienced members, to see if this approach is right or not.

I already handle the selection the way you mentioned it, so when I try to select an item, the event is passed to the controller which gives feedback to the Component and changes it’s appearance if the fact of selecting it is valid. No problem.

The only thing is when I have such a selection, how do I detect that the user clicked on one of the selected items and start dragging ?

My idea was to have an invisible component which stores a list of the selected components. Then I’d override the “hittest” method and make it return true, if hit test is true for any of the child components.

I don’t want to drag the components themselvs actually ! If I have many items selected, I want to detect that I’m in a drag-and-drop operation, then create a custom image, like an icon, which would be dragged. I would do that in this “invisible component” paint method (which, obviously, would make it visible ! :slight_smile: )

I had a look at the jucer code and it seems that it’s the kind of approach it has. Does it sound ok to you ?


#6

The file system is not a database, and a hierarchy of juce::Component objects is not Adobe Illustrator.


#7

Yes, that’s an approach I’ve used a lot, and it works pretty well.


#8

Vinn : Do you mean I’m overengineering ?

I don’t think I’m am, my explaination isn’t the best but I’m not a native english speaker you know, so sometimes I need way to many words to explain something simple :wink:

Julian : Thanks ! :slight_smile:


#9

Ok this technique works great ! I have an invisible component taking the whole window, and a custom “hitTest” on the selected components. So far so good.

my code looks like this :


bool SelectionComponent::hitTest( int x,int y )
{
	for (int i=0;componentsInSelection.size();i++)
	{
		if (componentsInSelection.getUnchecked(i)->hitTest(x,y)) // this is always true, as soon as I have at least one component in my array 
		{
			return true;
		}
	}
	return false;
		}

However, the hitTest always returns true, no matter which x/y coordinates I give it ?!

Looking in Juce code :

[code]bool Component::hitTest (int x, int y)
{
if (! flags.ignoresMouseClicksFlag)
return true;

[…]
[/code]

This flag isn’t set on my selecteds components, and while tracing, I always reach this "return true"
So far, I never had to worry about those flags.

Is it a “normal” behaviour ?
Should I use “contains” instead of “hitTest” on my selected components ?


#10

Yes, you should use contains() instead. As it says in the description of hitTest, the x and y parameters are assumed to be within the component’s bounds.