Menubar->addMouseListener


#1

When I have a component window with a menubar placed on it at top and a draggable component in the main window, if I add a mouse listener to the main window from the menubar and have the draggable thing act on the mouse movements, then theoretically if I drag the compononent window from the window itself, or the menubar (used false as the second parameter to not inherit mouse moves from menu buttons and such), then it should drag around, and it work if I do it from the main window, and it also works if I add a mouse listener to a label on that window and drag around, but if I add it to the menu bar, then the menubar does not seem to send the mouse down event or something. If I drag from anything else, then it drags from the point the mouse is clicked at properly, however, if I drag by clicking on the menu bar, then the component jumps to where the mouse was last time it was let go, if I hadn’t dragged yet at all, it jumps to 0, 0.

Make a component to make a window, make a menubar to cover the top full 22 or so pixels. Add a label somewhere else in the component window, add a component dragger and link it to the mousedown and mousedrag events of the component window as normal. Add a mouse listener to both the label and the menubar, with the listener being the component window and the second param being false (so it does not inherit through the entire tree of the mainbar). Start it up, click and drag from the component window background, or from the label, it works as expected. Close it and restart it, click from somewhere on the right part of the menubar, the component jumps so that 0,0 is where the mouse is pointing, dragging that works, but from that odd point, let go, and drag from the main component background, say in the lower section somewhere, it works as it should, then try dragging from the menubar again, the component jumps so that the last mousedragged position is now current. Now add some menu’s to the bar, try clicking through the menu’s, it drags the component oddely there as well, should not the false of the addMouseLisinter paramater prevent that?

I am not using dragging from the bar, just the main component, not a big deal, although a bit un-intuitive since you can drag from anywhere but the bar, when it is usually just the bar that you can drag from.

I am doing this through a termserv session so debugging the juce part would be difficult, I should be able to tonight however for a little bit if you wish.

EDIT: Also, would it be possible for you to add another param to the ComponentDragger that specifies whether it can go outside of the screen bounderies or not. Currently I have subclassed it and using my overridden in my classes (I did the above tests using the normal class). Not a big deal, but useful it would be. My overridden class:

[code]class TimeClockComponentDragger: public ComponentDragger
{
virtual void checkPosition(Component componentToDrag, int &newX, int &newY)
{
Rectangle limits;
Component
const p = componentToDrag->getParentComponent();

	if (p == 0)
	{
		limits = Desktop::getInstance().getAllMonitorDisplayAreas().getBounds();
		limits.setSize(limits.getWidth() - componentToDrag->getWidth(), limits.getHeight() - componentToDrag->getHeight());
	}
	else
	{
		limits.setSize (p->getWidth() - componentToDrag->getWidth(), p->getHeight() - componentToDrag->getHeight());
	}

	newX = jmax(newX, limits.getX());
	newX = jmin(newX, limits.getRight());
	newY = jmax(newY, limits.getY());
	newY = jmin(newY, limits.getBottom());
};

};[/code]
Putting that code chunk in the base checkPosition with a bool specifying which to use, which is what would be passed in through a function perhaps, to tell whether it can go outside of the screen edges or not.


#2

It sounds like the mouseDown event’s not getting sent to the dragger, but the mouseDrags are… I can’t see any reason for this, but guess it’s something to do with the menu bar being a global mouse listener (which is a method of the Desktop class). I’ll keep looking for clues…

And sure, that’s a good idea about beefing up the componentdragger class.


#3

It would also be a simple task to have it lock to the edges of the screen when it is allowed to go out, could add a function to set a tolerance, if 0(default) then it wouldn’t snap. Shall I edit this class to add these functionality when I get time?

EDIT: Okay, instead of sleeping I went ahead and did it, how’s this for the header:

[code]/*

This file is part of the JUCE library - "Jules’ Utility Class Extensions"
Copyright 2004-5 by Raw Material Software ltd.


JUCE can be redistributed and/or modified under the terms of the
GNU General Public License, as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

JUCE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with JUCE; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA


If you’d like to release a closed-source product which uses JUCE, commercial
licenses are also available: visit www.rawmaterialsoftware.com/juce for
more information.

==============================================================================
*/

//==============================================================================
/**
An object to take care of the logic for dragging components around with the mouse.

Very easy to use - in your mouseDown() callback, call startDraggingComponent(),
then in your mouseDrag() callback, call dragComponent().

By default this will stop the component being dragged too far outside its
parent component (or outside the desktop if it's a top-level window), but you
can implement your own custom boundaries by overriding the checkPosition() method.

e.g. @code
class MyDraggableComp
{
    ComponentDragger myDragger;

    void mouseDown (const MouseEvent& e)
    {
        myDragger.startDraggingComponent (this, e);
    }

    void mouseDrag (const MouseEvent& e)
    {
        myDragger.dragComponent (this, e);
    }
};
@endcode

/
class JUCE_API ComponentDragger
{
public:
//==============================================================================
/
* Creates a ComponentDragger. */
ComponentDragger();

/** Destructor. */
virtual ~ComponentDragger();

//==============================================================================
/** Call this from your component's mouseDown() method, to prepare for dragging.

    @param componentToDrag      the component that you want to drag
    @param e                    the current mouse-down event
    @see dragComponent
*/
void startDraggingComponent (Component* componentToDrag, const MouseEvent& e);

/** Call this from your mouseDrag() callback to move the component.

    This will move the component, but will first check the validity of the
    component's new position using the checkPosition() method, which you
    can override if you need to enforce special positioning limits on the
    component.

    @param componentToDrag      the component that you want to drag
    @param e                    the current mouse-drag event
    @see dragComponent
*/
void dragComponent (Component* componentToDrag, const MouseEvent& e);

/** This is called internally by dragComponent() to restrict the position
    that the component's being dragged to.

    By default this will limit the new position so that the component stays
    visible within its parent component (or the screen if it's on the
    desktop), but you may change this behaviour if necessary.

    @param componentToDrag      the component that's being moved
    @param newX                 the x position that the user's trying to drag it
                                to - you should change this variable if you need to
                                limit it somehow
    @param newY                 the y position that the user's trying to drag it
                                to - you should change this variable if you need to
                                limit it somehow
*/
virtual void checkPosition (Component* componentToDrag,
                            int& newX,
                            int& newY);

/** Call this to set whether or not this will allow the dragged component to be
dragged off it's parent or the desktop.

@param canBeDraggedOffParent    set to true to disallow moving off parent/screen,
                                true by default
*/
void setDraggableOffParent(bool canBeDraggedOffParent);

/** Returns true if it is currently set to allow dragging off the parent */
bool getDraggableOffParent() const;

/** Call this to set a snap to border tolerence

@param snapTolerance            the snap to parent/screen tolerance, or 0 to disable
*/
void setSnapTolerance(int snapTolerance);

/** Returns the current snap tolerance */
int getSnapTolerance() const;

/** Call this to set the minimum visible amount

@param minVisible               the minimum visible amount
*/
void setMinVisible(int minVisible);

/** Returns the current snap tolerance */
int getMinVisible() const;


//==============================================================================
juce_UseDebuggingNewOperator

private:
int mouseDownXOffset, mouseDownYOffset, minVisible, snapTolerance;
bool canBeDraggedOffParent;
};

[/code]

And how is this for the implimentation:

[code]/*

This file is part of the JUCE library - "Jules’ Utility Class Extensions"
Copyright 2004-5 by Raw Material Software ltd.


JUCE can be redistributed and/or modified under the terms of the
GNU General Public License, as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

JUCE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with JUCE; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA


If you’d like to release a closed-source product which uses JUCE, commercial
licenses are also available: visit www.rawmaterialsoftware.com/juce for
more information.

==============================================================================
*/

BEGIN_JUCE_NAMESPACE

//==============================================================================
ComponentDragger::ComponentDragger()
: mouseDownXOffset (0),
mouseDownYOffset (0),
minVisible (16),
snapTolerance (0),
canBeDraggedOffParent (true)
{
}

ComponentDragger::~ComponentDragger()
{
}

//==============================================================================
void ComponentDragger::startDraggingComponent (Component* componentToDrag, const MouseEvent& e)
{
jassert (componentToDrag->isValidComponent());

if (componentToDrag->isValidComponent())
{
    const MouseEvent e2 (e.getEventRelativeTo (componentToDrag));

    mouseDownXOffset = e2.x;
    mouseDownYOffset = e2.y;
}

}

void ComponentDragger::dragComponent (Component* componentToDrag, const MouseEvent& e)
{
jassert (componentToDrag->isValidComponent());

if (componentToDrag->isValidComponent())
{
    const MouseEvent e2 (e.getEventRelativeTo (componentToDrag));

    Component* const p = componentToDrag->getParentComponent();
    const int px = (p != 0) ? p->getScreenX() : 0;
    const int py = (p != 0) ? p->getScreenY() : 0;

    int x = e.getScreenX() - mouseDownXOffset - px;
    int y = e.getScreenY() - mouseDownYOffset - py;

    checkPosition (componentToDrag, x, y);

    componentToDrag->setTopLeftPosition (x, y);
}

}

void ComponentDragger::checkPosition (Component* componentToDrag, int& x, int& y)
{
Rectangle limits;
Component* const p = componentToDrag->getParentComponent();

if (p == 0)
{
	limits = Desktop::getInstance().getAllMonitorDisplayAreas().getBounds();
	limits.setSize (limits.getWidth() - componentToDrag->getWidth(), limits.getHeight() - componentToDrag->getHeight());
}
else
{
	limits.setSize (p->getWidth() - componentToDrag->getWidth(), p->getHeight() - componentToDrag->getHeight());
}

if(!canBeDraggedOffParent)
{
	x = jmax (x, limits.getX());
	x = jmin (x, limits.getRight());

	y = jmax (y, limits.getY());
	y = jmin (y, limits.getBottom());
}
else
{
	x = jmax (x, (limits.getX() - componentToDrag->getWidth()) + minVisible);
	x = jmin (x, (limits.getRight() + componentToDrag->getWidth()) - minVisible);

	y = jmax (y, (limits.getY() - componentToDrag->getHeight()) + minVisible);
	y = jmin (y, (limits.getBottom() + componentToDrag->getHeight()) - minVisible);
}

if (snapTolerance > 0)
{
	if (x < snapTolerance && x > -snapTolerance)
	{
		x = 0;
	}
	else if ( x > limits.getWidth() - snapTolerance &&
		      x < limits.getWidth() + snapTolerance )
	{
		x = limits.getWidth();
	}

	if (y < snapTolerance && y > -snapTolerance)
	{
		y = 0;
	}
	else if ( y > limits.getHeight() - snapTolerance &&
		      y < limits.getHeight() + snapTolerance )
	{
		y = limits.getHeight();
	}
}

}

void ComponentDragger::setDraggableOffParent(bool canBeDraggedOffParent)
{
this->canBeDraggedOffParent = canBeDraggedOffParent;
}

bool ComponentDragger::getDraggableOffParent() const
{
return canBeDraggedOffParent;
}

void ComponentDragger::setSnapTolerance(int snapTolerance)
{
if(snapTolerance>=0)
{
this->snapTolerance = snapTolerance;
}
}

int ComponentDragger::getSnapTolerance() const
{
return snapTolerance;
}

void ComponentDragger::setMinVisible(int minVisible)
{
if(minVisible>0) // should at least by one…
{
this->minVisible = minVisible;
}
}

int ComponentDragger::getMinVisible() const
{
return minVisible;
}

END_JUCE_NAMESPACE
[/code]

I did test all my added functions and changed features and they do work as I expected. The documentation is plain, but probobly sufficient, and it is properly doxygenated and should be following your coding patterns.

I have added this to my current juce library and am using it instead of my subclassed version above, works well in all my instances.

I started making it to be edge lockable for each direction, figured that was overkill, deleted it, started over with it being simple as is.


#4

Ah - I’d already done it, although without the snapping. If I have time today I’ll add some snapping options too.


#5

Just some basic things I thought of while looking through it was. Only took about 5 minutes anyway. :slight_smile:


#6