mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-13 00:04:19 +00:00
Added Animated App template and examples
This commit is contained in:
parent
fefcf7aca6
commit
ff6520a89a
1141 changed files with 438491 additions and 94 deletions
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
ComponentDragger::ComponentDragger() {}
|
||||
ComponentDragger::~ComponentDragger() {}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentDragger::startDraggingComponent (Component* const componentToDrag, const MouseEvent& e)
|
||||
{
|
||||
jassert (componentToDrag != nullptr);
|
||||
jassert (e.mods.isAnyMouseButtonDown()); // The event has to be a drag event!
|
||||
|
||||
if (componentToDrag != nullptr)
|
||||
mouseDownWithinTarget = e.getEventRelativeTo (componentToDrag).getMouseDownPosition();
|
||||
}
|
||||
|
||||
void ComponentDragger::dragComponent (Component* const componentToDrag, const MouseEvent& e,
|
||||
ComponentBoundsConstrainer* const constrainer)
|
||||
{
|
||||
jassert (componentToDrag != nullptr);
|
||||
jassert (e.mods.isAnyMouseButtonDown()); // The event has to be a drag event!
|
||||
|
||||
if (componentToDrag != nullptr)
|
||||
{
|
||||
Rectangle<int> bounds (componentToDrag->getBounds());
|
||||
|
||||
// If the component is a window, multiple mouse events can get queued while it's in the same position,
|
||||
// so their coordinates become wrong after the first one moves the window, so in that case, we'll use
|
||||
// the current mouse position instead of the one that the event contains...
|
||||
if (componentToDrag->isOnDesktop())
|
||||
bounds += componentToDrag->getLocalPoint (nullptr, e.source.getScreenPosition()).roundToInt() - mouseDownWithinTarget;
|
||||
else
|
||||
bounds += e.getEventRelativeTo (componentToDrag).getPosition() - mouseDownWithinTarget;
|
||||
|
||||
if (constrainer != nullptr)
|
||||
constrainer->setBoundsForComponent (componentToDrag, bounds, false, false, false, false);
|
||||
else
|
||||
componentToDrag->setBounds (bounds);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_COMPONENTDRAGGER_H_INCLUDED
|
||||
#define JUCE_COMPONENTDRAGGER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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().
|
||||
|
||||
When starting a drag, you can give it a ComponentBoundsConstrainer to use
|
||||
to limit the component's position and keep it on-screen.
|
||||
|
||||
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, nullptr);
|
||||
}
|
||||
};
|
||||
@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 mouse event that is triggering the drag
|
||||
@see dragComponent
|
||||
*/
|
||||
void startDraggingComponent (Component* componentToDrag,
|
||||
const MouseEvent& e);
|
||||
|
||||
/** Call this from your mouseDrag() callback to move the component.
|
||||
|
||||
This will move the component, using the given constrainer object to check
|
||||
the new position.
|
||||
|
||||
@param componentToDrag the component that you want to drag
|
||||
@param e the current mouse-drag event
|
||||
@param constrainer an optional constrainer object that should be used
|
||||
to apply limits to the component's position. Pass
|
||||
null if you don't want to contrain the movement.
|
||||
@see startDraggingComponent
|
||||
*/
|
||||
void dragComponent (Component* componentToDrag,
|
||||
const MouseEvent& e,
|
||||
ComponentBoundsConstrainer* constrainer);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Point<int> mouseDownWithinTarget;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentDragger)
|
||||
};
|
||||
|
||||
#endif // JUCE_COMPONENTDRAGGER_H_INCLUDED
|
||||
|
|
@ -0,0 +1,508 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
bool juce_performDragDropFiles (const StringArray&, const bool copyFiles, bool& shouldStop);
|
||||
bool juce_performDragDropText (const String&, bool& shouldStop);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class DragAndDropContainer::DragImageComponent : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
DragImageComponent (const Image& im,
|
||||
const var& desc,
|
||||
Component* const sourceComponent,
|
||||
Component* const mouseSource,
|
||||
DragAndDropContainer& ddc,
|
||||
Point<int> offset)
|
||||
: sourceDetails (desc, sourceComponent, Point<int>()),
|
||||
image (im), owner (ddc),
|
||||
mouseDragSource (mouseSource),
|
||||
imageOffset (offset),
|
||||
hasCheckedForExternalDrag (false)
|
||||
{
|
||||
setSize (im.getWidth(), im.getHeight());
|
||||
|
||||
if (mouseDragSource == nullptr)
|
||||
mouseDragSource = sourceComponent;
|
||||
|
||||
mouseDragSource->addMouseListener (this, false);
|
||||
|
||||
startTimer (200);
|
||||
|
||||
setInterceptsMouseClicks (false, false);
|
||||
setAlwaysOnTop (true);
|
||||
}
|
||||
|
||||
~DragImageComponent()
|
||||
{
|
||||
if (owner.dragImageComponent == this)
|
||||
owner.dragImageComponent.release();
|
||||
|
||||
if (mouseDragSource != nullptr)
|
||||
{
|
||||
mouseDragSource->removeMouseListener (this);
|
||||
|
||||
if (DragAndDropTarget* const current = getCurrentlyOver())
|
||||
if (current->isInterestedInDragSource (sourceDetails))
|
||||
current->itemDragExit (sourceDetails);
|
||||
}
|
||||
|
||||
owner.dragOperationEnded();
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (isOpaque())
|
||||
g.fillAll (Colours::white);
|
||||
|
||||
g.setOpacity (1.0f);
|
||||
g.drawImageAt (image, 0, 0);
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& e) override
|
||||
{
|
||||
if (e.originalComponent != this)
|
||||
{
|
||||
if (mouseDragSource != nullptr)
|
||||
mouseDragSource->removeMouseListener (this);
|
||||
|
||||
// (note: use a local copy of this in case the callback runs
|
||||
// a modal loop and deletes this object before the method completes)
|
||||
DragAndDropTarget::SourceDetails details (sourceDetails);
|
||||
DragAndDropTarget* finalTarget = nullptr;
|
||||
|
||||
const bool wasVisible = isVisible();
|
||||
setVisible (false);
|
||||
Component* unused;
|
||||
finalTarget = findTarget (e.getScreenPosition(), details.localPosition, unused);
|
||||
|
||||
if (wasVisible) // fade the component and remove it - it'll be deleted later by the timer callback
|
||||
dismissWithAnimation (finalTarget == nullptr);
|
||||
|
||||
if (Component* parent = getParentComponent())
|
||||
parent->removeChildComponent (this);
|
||||
|
||||
if (finalTarget != nullptr)
|
||||
{
|
||||
currentlyOverComp = nullptr;
|
||||
finalTarget->itemDropped (details);
|
||||
}
|
||||
|
||||
// careful - this object could now be deleted..
|
||||
}
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
if (e.originalComponent != this)
|
||||
updateLocation (true, e.getScreenPosition());
|
||||
}
|
||||
|
||||
void updateLocation (const bool canDoExternalDrag, Point<int> screenPos)
|
||||
{
|
||||
DragAndDropTarget::SourceDetails details (sourceDetails);
|
||||
|
||||
setNewScreenPos (screenPos);
|
||||
|
||||
Component* newTargetComp;
|
||||
DragAndDropTarget* const newTarget = findTarget (screenPos, details.localPosition, newTargetComp);
|
||||
|
||||
setVisible (newTarget == nullptr || newTarget->shouldDrawDragImageWhenOver());
|
||||
|
||||
if (newTargetComp != currentlyOverComp)
|
||||
{
|
||||
if (DragAndDropTarget* const lastTarget = getCurrentlyOver())
|
||||
if (details.sourceComponent != nullptr && lastTarget->isInterestedInDragSource (details))
|
||||
lastTarget->itemDragExit (details);
|
||||
|
||||
currentlyOverComp = newTargetComp;
|
||||
|
||||
if (newTarget != nullptr
|
||||
&& newTarget->isInterestedInDragSource (details))
|
||||
newTarget->itemDragEnter (details);
|
||||
}
|
||||
|
||||
sendDragMove (details);
|
||||
|
||||
if (canDoExternalDrag)
|
||||
{
|
||||
const Time now (Time::getCurrentTime());
|
||||
|
||||
if (getCurrentlyOver() != nullptr)
|
||||
lastTimeOverTarget = now;
|
||||
else if (now > lastTimeOverTarget + RelativeTime::milliseconds (700))
|
||||
checkForExternalDrag (details, screenPos);
|
||||
}
|
||||
|
||||
forceMouseCursorUpdate();
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
forceMouseCursorUpdate();
|
||||
|
||||
if (sourceDetails.sourceComponent == nullptr)
|
||||
{
|
||||
deleteSelf();
|
||||
}
|
||||
else if (! isMouseButtonDownAnywhere())
|
||||
{
|
||||
if (mouseDragSource != nullptr)
|
||||
mouseDragSource->removeMouseListener (this);
|
||||
|
||||
deleteSelf();
|
||||
}
|
||||
}
|
||||
|
||||
bool keyPressed (const KeyPress& key) override
|
||||
{
|
||||
if (key == KeyPress::escapeKey)
|
||||
{
|
||||
dismissWithAnimation (true);
|
||||
deleteSelf();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool canModalEventBeSentToComponent (const Component* targetComponent) override
|
||||
{
|
||||
return targetComponent == mouseDragSource;
|
||||
}
|
||||
|
||||
// (overridden to avoid beeps when dragging)
|
||||
void inputAttemptWhenModal() override {}
|
||||
|
||||
DragAndDropTarget::SourceDetails sourceDetails;
|
||||
|
||||
private:
|
||||
Image image;
|
||||
DragAndDropContainer& owner;
|
||||
WeakReference<Component> mouseDragSource, currentlyOverComp;
|
||||
const Point<int> imageOffset;
|
||||
bool hasCheckedForExternalDrag;
|
||||
Time lastTimeOverTarget;
|
||||
|
||||
void forceMouseCursorUpdate()
|
||||
{
|
||||
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
|
||||
}
|
||||
|
||||
DragAndDropTarget* getCurrentlyOver() const noexcept
|
||||
{
|
||||
return dynamic_cast<DragAndDropTarget*> (currentlyOverComp.get());
|
||||
}
|
||||
|
||||
static Component* findDesktopComponentBelow (Point<int> screenPos)
|
||||
{
|
||||
Desktop& desktop = Desktop::getInstance();
|
||||
|
||||
for (int i = desktop.getNumComponents(); --i >= 0;)
|
||||
{
|
||||
Component* c = desktop.getComponent(i);
|
||||
|
||||
if (Component* hit = c->getComponentAt (c->getLocalPoint (nullptr, screenPos)))
|
||||
return hit;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DragAndDropTarget* findTarget (Point<int> screenPos, Point<int>& relativePos,
|
||||
Component*& resultComponent) const
|
||||
{
|
||||
Component* hit = getParentComponent();
|
||||
|
||||
if (hit == nullptr)
|
||||
hit = findDesktopComponentBelow (screenPos);
|
||||
else
|
||||
hit = hit->getComponentAt (hit->getLocalPoint (nullptr, screenPos));
|
||||
|
||||
// (note: use a local copy of this in case the callback runs
|
||||
// a modal loop and deletes this object before the method completes)
|
||||
const DragAndDropTarget::SourceDetails details (sourceDetails);
|
||||
|
||||
while (hit != nullptr)
|
||||
{
|
||||
if (DragAndDropTarget* const ddt = dynamic_cast<DragAndDropTarget*> (hit))
|
||||
{
|
||||
if (ddt->isInterestedInDragSource (details))
|
||||
{
|
||||
relativePos = hit->getLocalPoint (nullptr, screenPos);
|
||||
resultComponent = hit;
|
||||
return ddt;
|
||||
}
|
||||
}
|
||||
|
||||
hit = hit->getParentComponent();
|
||||
}
|
||||
|
||||
resultComponent = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setNewScreenPos (Point<int> screenPos)
|
||||
{
|
||||
Point<int> newPos (screenPos - imageOffset);
|
||||
|
||||
if (Component* p = getParentComponent())
|
||||
newPos = p->getLocalPoint (nullptr, newPos);
|
||||
|
||||
setTopLeftPosition (newPos);
|
||||
}
|
||||
|
||||
void sendDragMove (DragAndDropTarget::SourceDetails& details) const
|
||||
{
|
||||
if (DragAndDropTarget* const target = getCurrentlyOver())
|
||||
if (target->isInterestedInDragSource (details))
|
||||
target->itemDragMove (details);
|
||||
}
|
||||
|
||||
struct ExternalDragAndDropMessage : public CallbackMessage
|
||||
{
|
||||
ExternalDragAndDropMessage (const StringArray& f, bool canMove)
|
||||
: files (f), canMoveFiles (canMove)
|
||||
{}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
DragAndDropContainer::performExternalDragDropOfFiles (files, canMoveFiles);
|
||||
}
|
||||
|
||||
private:
|
||||
StringArray files;
|
||||
bool canMoveFiles;
|
||||
};
|
||||
|
||||
void checkForExternalDrag (DragAndDropTarget::SourceDetails& details, Point<int> screenPos)
|
||||
{
|
||||
if (! hasCheckedForExternalDrag)
|
||||
{
|
||||
if (Desktop::getInstance().findComponentAt (screenPos) == nullptr)
|
||||
{
|
||||
hasCheckedForExternalDrag = true;
|
||||
StringArray files;
|
||||
bool canMoveFiles = false;
|
||||
|
||||
if (owner.shouldDropFilesWhenDraggedExternally (details, files, canMoveFiles)
|
||||
&& files.size() > 0
|
||||
&& ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown())
|
||||
{
|
||||
(new ExternalDragAndDropMessage (files, canMoveFiles))->post();
|
||||
deleteSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteSelf()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void dismissWithAnimation (const bool shouldSnapBack)
|
||||
{
|
||||
setVisible (true);
|
||||
ComponentAnimator& animator = Desktop::getInstance().getAnimator();
|
||||
|
||||
if (shouldSnapBack && sourceDetails.sourceComponent != nullptr)
|
||||
{
|
||||
const Point<int> target (sourceDetails.sourceComponent->localPointToGlobal (sourceDetails.sourceComponent->getLocalBounds().getCentre()));
|
||||
const Point<int> ourCentre (localPointToGlobal (getLocalBounds().getCentre()));
|
||||
|
||||
animator.animateComponent (this,
|
||||
getBounds() + (target - ourCentre),
|
||||
0.0f, 120,
|
||||
true, 1.0, 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
animator.fadeOut (this, 120);
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (DragImageComponent)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
DragAndDropContainer::DragAndDropContainer()
|
||||
{
|
||||
}
|
||||
|
||||
DragAndDropContainer::~DragAndDropContainer()
|
||||
{
|
||||
dragImageComponent = nullptr;
|
||||
}
|
||||
|
||||
void DragAndDropContainer::startDragging (const var& sourceDescription,
|
||||
Component* sourceComponent,
|
||||
Image dragImage,
|
||||
const bool allowDraggingToExternalWindows,
|
||||
const Point<int>* imageOffsetFromMouse)
|
||||
{
|
||||
if (dragImageComponent == nullptr)
|
||||
{
|
||||
MouseInputSource* const draggingSource = Desktop::getInstance().getDraggingMouseSource (0);
|
||||
|
||||
if (draggingSource == nullptr || ! draggingSource->isDragging())
|
||||
{
|
||||
jassertfalse; // You must call startDragging() from within a mouseDown or mouseDrag callback!
|
||||
return;
|
||||
}
|
||||
|
||||
const Point<int> lastMouseDown (draggingSource->getLastMouseDownPosition().roundToInt());
|
||||
Point<int> imageOffset;
|
||||
|
||||
if (dragImage.isNull())
|
||||
{
|
||||
dragImage = sourceComponent->createComponentSnapshot (sourceComponent->getLocalBounds())
|
||||
.convertedToFormat (Image::ARGB);
|
||||
|
||||
dragImage.multiplyAllAlphas (0.6f);
|
||||
|
||||
const int lo = 150;
|
||||
const int hi = 400;
|
||||
|
||||
Point<int> relPos (sourceComponent->getLocalPoint (nullptr, lastMouseDown));
|
||||
Point<int> clipped (dragImage.getBounds().getConstrainedPoint (relPos));
|
||||
Random random;
|
||||
|
||||
for (int y = dragImage.getHeight(); --y >= 0;)
|
||||
{
|
||||
const double dy = (y - clipped.getY()) * (y - clipped.getY());
|
||||
|
||||
for (int x = dragImage.getWidth(); --x >= 0;)
|
||||
{
|
||||
const int dx = x - clipped.getX();
|
||||
const int distance = roundToInt (std::sqrt (dx * dx + dy));
|
||||
|
||||
if (distance > lo)
|
||||
{
|
||||
const float alpha = (distance > hi) ? 0
|
||||
: (hi - distance) / (float) (hi - lo)
|
||||
+ random.nextFloat() * 0.008f;
|
||||
|
||||
dragImage.multiplyAlphaAt (x, y, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imageOffset = clipped;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (imageOffsetFromMouse == nullptr)
|
||||
imageOffset = dragImage.getBounds().getCentre();
|
||||
else
|
||||
imageOffset = dragImage.getBounds().getConstrainedPoint (-*imageOffsetFromMouse);
|
||||
}
|
||||
|
||||
dragImageComponent = new DragImageComponent (dragImage, sourceDescription, sourceComponent,
|
||||
draggingSource->getComponentUnderMouse(), *this, imageOffset);
|
||||
|
||||
if (allowDraggingToExternalWindows)
|
||||
{
|
||||
if (! Desktop::canUseSemiTransparentWindows())
|
||||
dragImageComponent->setOpaque (true);
|
||||
|
||||
dragImageComponent->addToDesktop (ComponentPeer::windowIgnoresMouseClicks
|
||||
| ComponentPeer::windowIsTemporary
|
||||
| ComponentPeer::windowIgnoresKeyPresses);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Component* const thisComp = dynamic_cast<Component*> (this))
|
||||
{
|
||||
thisComp->addChildComponent (dragImageComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse; // Your DragAndDropContainer needs to be a Component!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static_cast<DragImageComponent*> (dragImageComponent.get())->updateLocation (false, lastMouseDown);
|
||||
dragImageComponent->enterModalState();
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
// Under heavy load, the layered window's paint callback can often be lost by the OS,
|
||||
// so forcing a repaint at least once makes sure that the window becomes visible..
|
||||
if (ComponentPeer* const peer = dragImageComponent->getPeer())
|
||||
peer->performAnyPendingRepaintsNow();
|
||||
#endif
|
||||
|
||||
dragOperationStarted();
|
||||
}
|
||||
}
|
||||
|
||||
bool DragAndDropContainer::isDragAndDropActive() const
|
||||
{
|
||||
return dragImageComponent != nullptr;
|
||||
}
|
||||
|
||||
var DragAndDropContainer::getCurrentDragDescription() const
|
||||
{
|
||||
return dragImageComponent != nullptr ? dragImageComponent->sourceDetails.description
|
||||
: var();
|
||||
}
|
||||
|
||||
DragAndDropContainer* DragAndDropContainer::findParentDragContainerFor (Component* c)
|
||||
{
|
||||
return c != nullptr ? c->findParentComponentOfClass<DragAndDropContainer>() : nullptr;
|
||||
}
|
||||
|
||||
bool DragAndDropContainer::shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails&, StringArray&, bool&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void DragAndDropContainer::dragOperationStarted() {}
|
||||
void DragAndDropContainer::dragOperationEnded() {}
|
||||
|
||||
//==============================================================================
|
||||
DragAndDropTarget::SourceDetails::SourceDetails (const var& desc, Component* comp, Point<int> pos) noexcept
|
||||
: description (desc),
|
||||
sourceComponent (comp),
|
||||
localPosition (pos)
|
||||
{
|
||||
}
|
||||
|
||||
void DragAndDropTarget::itemDragEnter (const SourceDetails&) {}
|
||||
void DragAndDropTarget::itemDragMove (const SourceDetails&) {}
|
||||
void DragAndDropTarget::itemDragExit (const SourceDetails&) {}
|
||||
bool DragAndDropTarget::shouldDrawDragImageWhenOver() { return true; }
|
||||
|
||||
//==============================================================================
|
||||
void FileDragAndDropTarget::fileDragEnter (const StringArray&, int, int) {}
|
||||
void FileDragAndDropTarget::fileDragMove (const StringArray&, int, int) {}
|
||||
void FileDragAndDropTarget::fileDragExit (const StringArray&) {}
|
||||
|
||||
void TextDragAndDropTarget::textDragEnter (const String&, int, int) {}
|
||||
void TextDragAndDropTarget::textDragMove (const String&, int, int) {}
|
||||
void TextDragAndDropTarget::textDragExit (const String&) {}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAGANDDROPCONTAINER_H_INCLUDED
|
||||
#define JUCE_DRAGANDDROPCONTAINER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Enables drag-and-drop behaviour for a component and all its sub-components.
|
||||
|
||||
For a component to be able to make or receive drag-and-drop events, one of its parent
|
||||
components must derive from this class. It's probably best for the top-level
|
||||
component to implement it.
|
||||
|
||||
Then to start a drag operation, any sub-component can just call the startDragging()
|
||||
method, and this object will take over, tracking the mouse and sending appropriate
|
||||
callbacks to any child components derived from DragAndDropTarget which the mouse
|
||||
moves over.
|
||||
|
||||
Note: If all that you need to do is to respond to files being drag-and-dropped from
|
||||
the operating system onto your component, you don't need any of these classes: you can do this
|
||||
simply by overriding Component::filesDropped().
|
||||
|
||||
@see DragAndDropTarget
|
||||
*/
|
||||
class JUCE_API DragAndDropContainer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DragAndDropContainer.
|
||||
|
||||
The object that derives from this class must also be a Component.
|
||||
*/
|
||||
DragAndDropContainer();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~DragAndDropContainer();
|
||||
|
||||
//==============================================================================
|
||||
/** Begins a drag-and-drop operation.
|
||||
|
||||
This starts a drag-and-drop operation - call it when the user drags the
|
||||
mouse in your drag-source component, and this object will track mouse
|
||||
movements until the user lets go of the mouse button, and will send
|
||||
appropriate messages to DragAndDropTarget objects that the mouse moves
|
||||
over.
|
||||
|
||||
findParentDragContainerFor() is a handy method to call to find the
|
||||
drag container to use for a component.
|
||||
|
||||
@param sourceDescription a string or value to use as the description of the thing being dragged -
|
||||
this will be passed to the objects that might be dropped-onto so they can
|
||||
decide whether they want to handle it
|
||||
@param sourceComponent the component that is being dragged
|
||||
@param dragImage the image to drag around underneath the mouse. If this is a null image,
|
||||
a snapshot of the sourceComponent will be used instead.
|
||||
@param allowDraggingToOtherJuceWindows if true, the dragged component will appear as a desktop
|
||||
window, and can be dragged to DragAndDropTargets that are the
|
||||
children of components other than this one.
|
||||
@param imageOffsetFromMouse if an image has been passed-in, this specifies the offset
|
||||
at which the image should be drawn from the mouse. If it isn't
|
||||
specified, then the image will be centred around the mouse. If
|
||||
an image hasn't been passed-in, this will be ignored.
|
||||
*/
|
||||
void startDragging (const var& sourceDescription,
|
||||
Component* sourceComponent,
|
||||
Image dragImage = Image::null,
|
||||
bool allowDraggingToOtherJuceWindows = false,
|
||||
const Point<int>* imageOffsetFromMouse = nullptr);
|
||||
|
||||
/** Returns true if something is currently being dragged. */
|
||||
bool isDragAndDropActive() const;
|
||||
|
||||
/** Returns the description of the thing that's currently being dragged.
|
||||
|
||||
If nothing's being dragged, this will return a null var, otherwise it'll return
|
||||
the var that was passed into startDragging().
|
||||
|
||||
@see startDragging
|
||||
*/
|
||||
var getCurrentDragDescription() const;
|
||||
|
||||
/** Utility to find the DragAndDropContainer for a given Component.
|
||||
|
||||
This will search up this component's parent hierarchy looking for the first
|
||||
parent component which is a DragAndDropContainer.
|
||||
|
||||
It's useful when a component wants to call startDragging but doesn't know
|
||||
the DragAndDropContainer it should to use.
|
||||
|
||||
Obviously this may return nullptr if it doesn't find a suitable component.
|
||||
*/
|
||||
static DragAndDropContainer* findParentDragContainerFor (Component* childComponent);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** This performs a synchronous drag-and-drop of a set of files to some external
|
||||
application.
|
||||
|
||||
You can call this function in response to a mouseDrag callback, and it will
|
||||
block, running its own internal message loop and tracking the mouse, while it
|
||||
uses a native operating system drag-and-drop operation to move or copy some
|
||||
files to another application.
|
||||
|
||||
@param files a list of filenames to drag
|
||||
@param canMoveFiles if true, the app that receives the files is allowed to move the files to a new location
|
||||
(if this is appropriate). If false, the receiver is expected to make a copy of them.
|
||||
@returns true if the files were successfully dropped somewhere, or false if it
|
||||
was interrupted
|
||||
@see performExternalDragDropOfText
|
||||
*/
|
||||
static bool performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles);
|
||||
|
||||
/** This performs a synchronous drag-and-drop of a block of text to some external
|
||||
application.
|
||||
|
||||
You can call this function in response to a mouseDrag callback, and it will
|
||||
block, running its own internal message loop and tracking the mouse, while it
|
||||
uses a native operating system drag-and-drop operation to move or copy some
|
||||
text to another application.
|
||||
|
||||
@param text the text to copy
|
||||
@returns true if the text was successfully dropped somewhere, or false if it
|
||||
was interrupted
|
||||
@see performExternalDragDropOfFiles
|
||||
*/
|
||||
static bool performExternalDragDropOfText (const String& text);
|
||||
|
||||
protected:
|
||||
/** Override this if you want to be able to perform an external drag a set of files
|
||||
when the user drags outside of this container component.
|
||||
|
||||
This method will be called when a drag operation moves outside the Juce-based window,
|
||||
and if you want it to then perform a file drag-and-drop, add the filenames you want
|
||||
to the array passed in, and return true.
|
||||
|
||||
@param sourceDetails information about the source of the drag operation
|
||||
@param files on return, the filenames you want to drag
|
||||
@param canMoveFiles on return, true if it's ok for the receiver to move the files; false if
|
||||
it must make a copy of them (see the performExternalDragDropOfFiles() method)
|
||||
@see performExternalDragDropOfFiles
|
||||
*/
|
||||
virtual bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails,
|
||||
StringArray& files, bool& canMoveFiles);
|
||||
|
||||
/** Subclasses can override this to be told when a drag starts. */
|
||||
virtual void dragOperationStarted();
|
||||
|
||||
/** Subclasses can override this to be told when a drag finishes. */
|
||||
virtual void dragOperationEnded();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class DragImageComponent;
|
||||
friend class DragImageComponent;
|
||||
friend struct ContainerDeletePolicy<DragImageComponent>;
|
||||
ScopedPointer<DragImageComponent> dragImageComponent;
|
||||
|
||||
JUCE_DEPRECATED (virtual bool shouldDropFilesWhenDraggedExternally (const String&, Component*, StringArray&, bool&)) { return false; }
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DragAndDropContainer)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DRAGANDDROPCONTAINER_H_INCLUDED
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DRAGANDDROPTARGET_H_INCLUDED
|
||||
#define JUCE_DRAGANDDROPTARGET_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Components derived from this class can have things dropped onto them by a DragAndDropContainer.
|
||||
|
||||
To create a component that can receive things drag-and-dropped by a DragAndDropContainer,
|
||||
derive your component from this class, and make sure that it is somewhere inside a
|
||||
DragAndDropContainer component.
|
||||
|
||||
Note: If all that you need to do is to respond to files being drag-and-dropped from
|
||||
the operating system onto your component, you don't need any of these classes: instead
|
||||
see the FileDragAndDropTarget class.
|
||||
|
||||
@see DragAndDropContainer, FileDragAndDropTarget
|
||||
*/
|
||||
class JUCE_API DragAndDropTarget
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~DragAndDropTarget() {}
|
||||
|
||||
//==============================================================================
|
||||
/** Contains details about the source of a drag-and-drop operation.
|
||||
The contents of this
|
||||
*/
|
||||
class JUCE_API SourceDetails
|
||||
{
|
||||
public:
|
||||
/** Creates a SourceDetails object from its various settings. */
|
||||
SourceDetails (const var& description,
|
||||
Component* sourceComponent,
|
||||
Point<int> localPosition) noexcept;
|
||||
|
||||
/** A descriptor for the drag - this is set DragAndDropContainer::startDragging(). */
|
||||
var description;
|
||||
|
||||
/** The component from the drag operation was started. */
|
||||
WeakReference<Component> sourceComponent;
|
||||
|
||||
/** The local position of the mouse, relative to the target component.
|
||||
Note that for calls such as isInterestedInDragSource(), this may be a null position.
|
||||
*/
|
||||
Point<int> localPosition;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Callback to check whether this target is interested in the type of object being
|
||||
dragged.
|
||||
|
||||
@param dragSourceDetails contains information about the source of the drag operation.
|
||||
@returns true if this component wants to receive the other callbacks regarging this
|
||||
type of object; if it returns false, no other callbacks will be made.
|
||||
*/
|
||||
virtual bool isInterestedInDragSource (const SourceDetails& dragSourceDetails) = 0;
|
||||
|
||||
/** Callback to indicate that something is being dragged over this component.
|
||||
|
||||
This gets called when the user moves the mouse into this component while dragging
|
||||
something.
|
||||
|
||||
Use this callback as a trigger to make your component repaint itself to give the
|
||||
user feedback about whether the item can be dropped here or not.
|
||||
|
||||
@param dragSourceDetails contains information about the source of the drag operation.
|
||||
@see itemDragExit
|
||||
*/
|
||||
virtual void itemDragEnter (const SourceDetails& dragSourceDetails);
|
||||
|
||||
/** Callback to indicate that the user is dragging something over this component.
|
||||
|
||||
This gets called when the user moves the mouse over this component while dragging
|
||||
something. Normally overriding itemDragEnter() and itemDragExit() are enough, but
|
||||
this lets you know what happens in-between.
|
||||
|
||||
@param dragSourceDetails contains information about the source of the drag operation.
|
||||
*/
|
||||
virtual void itemDragMove (const SourceDetails& dragSourceDetails);
|
||||
|
||||
/** Callback to indicate that something has been dragged off the edge of this component.
|
||||
|
||||
This gets called when the user moves the mouse out of this component while dragging
|
||||
something.
|
||||
|
||||
If you've used itemDragEnter() to repaint your component and give feedback, use this
|
||||
as a signal to repaint it in its normal state.
|
||||
|
||||
@param dragSourceDetails contains information about the source of the drag operation.
|
||||
@see itemDragEnter
|
||||
*/
|
||||
virtual void itemDragExit (const SourceDetails& dragSourceDetails);
|
||||
|
||||
/** Callback to indicate that the user has dropped something onto this component.
|
||||
|
||||
When the user drops an item this get called, and you can use the description to
|
||||
work out whether your object wants to deal with it or not.
|
||||
|
||||
Note that after this is called, the itemDragExit method may not be called, so you should
|
||||
clean up in here if there's anything you need to do when the drag finishes.
|
||||
|
||||
@param dragSourceDetails contains information about the source of the drag operation.
|
||||
*/
|
||||
virtual void itemDropped (const SourceDetails& dragSourceDetails) = 0;
|
||||
|
||||
/** Overriding this allows the target to tell the drag container whether to
|
||||
draw the drag image while the cursor is over it.
|
||||
|
||||
By default it returns true, but if you return false, then the normal drag
|
||||
image will not be shown when the cursor is over this target.
|
||||
*/
|
||||
virtual bool shouldDrawDragImageWhenOver();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
// The parameters for these methods have changed - please update your code!
|
||||
virtual void isInterestedInDragSource (const String&, Component*) {}
|
||||
virtual int itemDragEnter (const String&, Component*, int, int) { return 0; }
|
||||
virtual int itemDragMove (const String&, Component*, int, int) { return 0; }
|
||||
virtual int itemDragExit (const String&, Component*) { return 0; }
|
||||
virtual int itemDropped (const String&, Component*, int, int) { return 0; }
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // JUCE_DRAGANDDROPTARGET_H_INCLUDED
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_FILEDRAGANDDROPTARGET_H_INCLUDED
|
||||
#define JUCE_FILEDRAGANDDROPTARGET_H_INCLUDED
|
||||
|
||||
/**
|
||||
Components derived from this class can have files dropped onto them by an external application.
|
||||
|
||||
@see DragAndDropContainer
|
||||
*/
|
||||
class JUCE_API FileDragAndDropTarget
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~FileDragAndDropTarget() {}
|
||||
|
||||
/** Callback to check whether this target is interested in the set of files being offered.
|
||||
|
||||
Note that this will be called repeatedly when the user is dragging the mouse around over your
|
||||
component, so don't do anything time-consuming in here, like opening the files to have a look
|
||||
inside them!
|
||||
|
||||
@param files the set of (absolute) pathnames of the files that the user is dragging
|
||||
@returns true if this component wants to receive the other callbacks regarging this
|
||||
type of object; if it returns false, no other callbacks will be made.
|
||||
*/
|
||||
virtual bool isInterestedInFileDrag (const StringArray& files) = 0;
|
||||
|
||||
/** Callback to indicate that some files are being dragged over this component.
|
||||
|
||||
This gets called when the user moves the mouse into this component while dragging.
|
||||
|
||||
Use this callback as a trigger to make your component repaint itself to give the
|
||||
user feedback about whether the files can be dropped here or not.
|
||||
|
||||
@param files the set of (absolute) pathnames of the files that the user is dragging
|
||||
@param x the mouse x position, relative to this component
|
||||
@param y the mouse y position, relative to this component
|
||||
*/
|
||||
virtual void fileDragEnter (const StringArray& files, int x, int y);
|
||||
|
||||
/** Callback to indicate that the user is dragging some files over this component.
|
||||
|
||||
This gets called when the user moves the mouse over this component while dragging.
|
||||
Normally overriding itemDragEnter() and itemDragExit() are enough, but
|
||||
this lets you know what happens in-between.
|
||||
|
||||
@param files the set of (absolute) pathnames of the files that the user is dragging
|
||||
@param x the mouse x position, relative to this component
|
||||
@param y the mouse y position, relative to this component
|
||||
*/
|
||||
virtual void fileDragMove (const StringArray& files, int x, int y);
|
||||
|
||||
/** Callback to indicate that the mouse has moved away from this component.
|
||||
|
||||
This gets called when the user moves the mouse out of this component while dragging
|
||||
the files.
|
||||
|
||||
If you've used fileDragEnter() to repaint your component and give feedback, use this
|
||||
as a signal to repaint it in its normal state.
|
||||
|
||||
@param files the set of (absolute) pathnames of the files that the user is dragging
|
||||
*/
|
||||
virtual void fileDragExit (const StringArray& files);
|
||||
|
||||
/** Callback to indicate that the user has dropped the files onto this component.
|
||||
|
||||
When the user drops the files, this get called, and you can use the files in whatever
|
||||
way is appropriate.
|
||||
|
||||
Note that after this is called, the fileDragExit method may not be called, so you should
|
||||
clean up in here if there's anything you need to do when the drag finishes.
|
||||
|
||||
@param files the set of (absolute) pathnames of the files that the user is dragging
|
||||
@param x the mouse x position, relative to this component
|
||||
@param y the mouse y position, relative to this component
|
||||
*/
|
||||
virtual void filesDropped (const StringArray& files, int x, int y) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_FILEDRAGANDDROPTARGET_H_INCLUDED
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_LASSOCOMPONENT_H_INCLUDED
|
||||
#define JUCE_LASSOCOMPONENT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class used by the LassoComponent to manage the things that it selects.
|
||||
|
||||
This allows the LassoComponent to find out which items are within the lasso,
|
||||
and to change the list of selected items.
|
||||
|
||||
@see LassoComponent, SelectedItemSet
|
||||
*/
|
||||
template <class SelectableItemType>
|
||||
class LassoSource
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~LassoSource() {}
|
||||
|
||||
/** Returns the set of items that lie within a given lassoable region.
|
||||
|
||||
Your implementation of this method must find all the relevent items that lie
|
||||
within the given rectangle. and add them to the itemsFound array.
|
||||
|
||||
The coordinates are relative to the top-left of the lasso component's parent
|
||||
component. (i.e. they are the same as the size and position of the lasso
|
||||
component itself).
|
||||
*/
|
||||
virtual void findLassoItemsInArea (Array <SelectableItemType>& itemsFound,
|
||||
const Rectangle<int>& area) = 0;
|
||||
|
||||
/** Returns the SelectedItemSet that the lasso should update.
|
||||
|
||||
This set will be continuously updated by the LassoComponent as it gets
|
||||
dragged around, so make sure that you've got a ChangeListener attached to
|
||||
the set so that your UI objects will know when the selection changes and
|
||||
be able to update themselves appropriately.
|
||||
*/
|
||||
virtual SelectedItemSet<SelectableItemType>& getLassoSelection() = 0;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that acts as a rectangular selection region, which you drag with
|
||||
the mouse to select groups of objects (in conjunction with a SelectedItemSet).
|
||||
|
||||
To use one of these:
|
||||
|
||||
- In your mouseDown or mouseDrag event, add the LassoComponent to your parent
|
||||
component, and call its beginLasso() method, giving it a
|
||||
suitable LassoSource object that it can use to find out which items are in
|
||||
the active area.
|
||||
|
||||
- Each time your parent component gets a mouseDrag event, call dragLasso()
|
||||
to update the lasso's position - it will use its LassoSource to calculate and
|
||||
update the current selection.
|
||||
|
||||
- After the drag has finished and you get a mouseUp callback, you should call
|
||||
endLasso() to clean up. This will make the lasso component invisible, and you
|
||||
can remove it from the parent component, or delete it.
|
||||
|
||||
The class takes into account the modifier keys that are being held down while
|
||||
the lasso is being dragged, so if shift is pressed, then any lassoed items will
|
||||
be added to the original selection; if ctrl or command is pressed, they will be
|
||||
xor'ed with any previously selected items.
|
||||
|
||||
@see LassoSource, SelectedItemSet
|
||||
*/
|
||||
template <class SelectableItemType>
|
||||
class LassoComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a Lasso component. */
|
||||
LassoComponent() : source (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Call this in your mouseDown event, to initialise a drag.
|
||||
|
||||
Pass in a suitable LassoSource object which the lasso will use to find
|
||||
the items and change the selection.
|
||||
|
||||
After using this method to initialise the lasso, repeatedly call dragLasso()
|
||||
in your component's mouseDrag callback.
|
||||
|
||||
@see dragLasso, endLasso, LassoSource
|
||||
*/
|
||||
void beginLasso (const MouseEvent& e, LassoSource<SelectableItemType>* lassoSource)
|
||||
{
|
||||
jassert (source == nullptr); // this suggests that you didn't call endLasso() after the last drag...
|
||||
jassert (lassoSource != nullptr); // the source can't be null!
|
||||
jassert (getParentComponent() != nullptr); // you need to add this to a parent component for it to work!
|
||||
|
||||
source = lassoSource;
|
||||
|
||||
if (lassoSource != nullptr)
|
||||
originalSelection = lassoSource->getLassoSelection().getItemArray();
|
||||
|
||||
setSize (0, 0);
|
||||
dragStartPos = e.getMouseDownPosition();
|
||||
}
|
||||
|
||||
/** Call this in your mouseDrag event, to update the lasso's position.
|
||||
|
||||
This must be repeatedly calling when the mouse is dragged, after you've
|
||||
first initialised the lasso with beginLasso().
|
||||
|
||||
This method takes into account the modifier keys that are being held down, so
|
||||
if shift is pressed, then the lassoed items will be added to any that were
|
||||
previously selected; if ctrl or command is pressed, then they will be xor'ed
|
||||
with previously selected items.
|
||||
|
||||
@see beginLasso, endLasso
|
||||
*/
|
||||
void dragLasso (const MouseEvent& e)
|
||||
{
|
||||
if (source != nullptr)
|
||||
{
|
||||
setBounds (Rectangle<int> (dragStartPos, e.getPosition()));
|
||||
setVisible (true);
|
||||
|
||||
Array<SelectableItemType> itemsInLasso;
|
||||
source->findLassoItemsInArea (itemsInLasso, getBounds());
|
||||
|
||||
if (e.mods.isShiftDown())
|
||||
{
|
||||
itemsInLasso.removeValuesIn (originalSelection); // to avoid duplicates
|
||||
itemsInLasso.addArray (originalSelection);
|
||||
}
|
||||
else if (e.mods.isCommandDown() || e.mods.isAltDown())
|
||||
{
|
||||
Array<SelectableItemType> originalMinusNew (originalSelection);
|
||||
originalMinusNew.removeValuesIn (itemsInLasso);
|
||||
|
||||
itemsInLasso.removeValuesIn (originalSelection);
|
||||
itemsInLasso.addArray (originalMinusNew);
|
||||
}
|
||||
|
||||
source->getLassoSelection() = SelectedItemSet<SelectableItemType> (itemsInLasso);
|
||||
}
|
||||
}
|
||||
|
||||
/** Call this in your mouseUp event, after the lasso has been dragged.
|
||||
@see beginLasso, dragLasso
|
||||
*/
|
||||
void endLasso()
|
||||
{
|
||||
source = nullptr;
|
||||
originalSelection.clear();
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the label.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
Note that you can also use the constants from TextEditor::ColourIds to change the
|
||||
colour of the text editor that is opened when a label is editable.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
lassoFillColourId = 0x1000440, /**< The colour to fill the lasso rectangle with. */
|
||||
lassoOutlineColourId = 0x1000441, /**< The colour to draw the outline with. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
getLookAndFeel().drawLasso (g, *this);
|
||||
|
||||
// this suggests that you've left a lasso comp lying around after the
|
||||
// mouse drag has finished.. Be careful to call endLasso() when you get a
|
||||
// mouse-up event.
|
||||
jassert (isMouseButtonDownAnywhere());
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
bool hitTest (int, int) override { return false; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array<SelectableItemType> originalSelection;
|
||||
LassoSource<SelectableItemType>* source;
|
||||
Point<int> dragStartPos;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LassoComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_LASSOCOMPONENT_H_INCLUDED
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
struct CustomMouseCursorInfo
|
||||
{
|
||||
CustomMouseCursorInfo (const Image& im, int hsX, int hsY) noexcept
|
||||
: image (im), hotspot (hsX, hsY), scaleFactor (1.0f)
|
||||
{}
|
||||
|
||||
CustomMouseCursorInfo (const Image& im, Point<int> hs, float scale) noexcept
|
||||
: image (im), hotspot (hs), scaleFactor (scale)
|
||||
{}
|
||||
|
||||
void* create() const;
|
||||
|
||||
Image image;
|
||||
const Point<int> hotspot;
|
||||
float scaleFactor;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (CustomMouseCursorInfo)
|
||||
};
|
||||
|
||||
class MouseCursor::SharedCursorHandle
|
||||
{
|
||||
public:
|
||||
explicit SharedCursorHandle (const MouseCursor::StandardCursorType type)
|
||||
: handle (createStandardMouseCursor (type)),
|
||||
refCount (1),
|
||||
standardType (type),
|
||||
isStandard (true)
|
||||
{
|
||||
}
|
||||
|
||||
SharedCursorHandle (const Image& image, Point<int> hotSpot, const float scaleFactor)
|
||||
: handle (CustomMouseCursorInfo (image, hotSpot, scaleFactor).create()),
|
||||
refCount (1),
|
||||
standardType (MouseCursor::NormalCursor),
|
||||
isStandard (false)
|
||||
{
|
||||
}
|
||||
|
||||
~SharedCursorHandle()
|
||||
{
|
||||
deleteMouseCursor (handle, isStandard);
|
||||
}
|
||||
|
||||
static SharedCursorHandle* createStandard (const MouseCursor::StandardCursorType type)
|
||||
{
|
||||
jassert (isPositiveAndBelow (type, MouseCursor::NumStandardCursorTypes));
|
||||
|
||||
const SpinLock::ScopedLockType sl (lock);
|
||||
|
||||
SharedCursorHandle*& c = getSharedCursor (type);
|
||||
|
||||
if (c == nullptr)
|
||||
c = new SharedCursorHandle (type);
|
||||
else
|
||||
c->retain();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool isStandardType (MouseCursor::StandardCursorType type) const noexcept
|
||||
{
|
||||
return type == standardType && isStandard;
|
||||
}
|
||||
|
||||
SharedCursorHandle* retain() noexcept
|
||||
{
|
||||
++refCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (--refCount == 0)
|
||||
{
|
||||
if (isStandard)
|
||||
{
|
||||
const SpinLock::ScopedLockType sl (lock);
|
||||
getSharedCursor (standardType) = nullptr;
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void* getHandle() const noexcept { return handle; }
|
||||
|
||||
private:
|
||||
void* const handle;
|
||||
Atomic <int> refCount;
|
||||
const MouseCursor::StandardCursorType standardType;
|
||||
const bool isStandard;
|
||||
static SpinLock lock;
|
||||
|
||||
static SharedCursorHandle*& getSharedCursor (const MouseCursor::StandardCursorType type)
|
||||
{
|
||||
static SharedCursorHandle* cursors [MouseCursor::NumStandardCursorTypes] = {};
|
||||
return cursors [type];
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedCursorHandle)
|
||||
};
|
||||
|
||||
SpinLock MouseCursor::SharedCursorHandle::lock;
|
||||
|
||||
//==============================================================================
|
||||
MouseCursor::MouseCursor() noexcept
|
||||
: cursorHandle (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
MouseCursor::MouseCursor (const StandardCursorType type)
|
||||
: cursorHandle (type != MouseCursor::NormalCursor ? SharedCursorHandle::createStandard (type) : nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
MouseCursor::MouseCursor (const Image& image, const int hotSpotX, const int hotSpotY)
|
||||
: cursorHandle (new SharedCursorHandle (image, Point<int> (hotSpotX, hotSpotY), 1.0f))
|
||||
{
|
||||
}
|
||||
|
||||
MouseCursor::MouseCursor (const Image& image, const int hotSpotX, const int hotSpotY, float scaleFactor)
|
||||
: cursorHandle (new SharedCursorHandle (image, Point<int> (hotSpotX, hotSpotY), scaleFactor))
|
||||
{
|
||||
}
|
||||
|
||||
MouseCursor::MouseCursor (const MouseCursor& other)
|
||||
: cursorHandle (other.cursorHandle == nullptr ? nullptr : other.cursorHandle->retain())
|
||||
{
|
||||
}
|
||||
|
||||
MouseCursor::~MouseCursor()
|
||||
{
|
||||
if (cursorHandle != nullptr)
|
||||
cursorHandle->release();
|
||||
}
|
||||
|
||||
MouseCursor& MouseCursor::operator= (const MouseCursor& other)
|
||||
{
|
||||
if (other.cursorHandle != nullptr)
|
||||
other.cursorHandle->retain();
|
||||
|
||||
if (cursorHandle != nullptr)
|
||||
cursorHandle->release();
|
||||
|
||||
cursorHandle = other.cursorHandle;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
MouseCursor::MouseCursor (MouseCursor&& other) noexcept
|
||||
: cursorHandle (other.cursorHandle)
|
||||
{
|
||||
other.cursorHandle = nullptr;
|
||||
}
|
||||
|
||||
MouseCursor& MouseCursor::operator= (MouseCursor&& other) noexcept
|
||||
{
|
||||
std::swap (cursorHandle, other.cursorHandle);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool MouseCursor::operator== (const MouseCursor& other) const noexcept
|
||||
{
|
||||
return getHandle() == other.getHandle();
|
||||
}
|
||||
|
||||
bool MouseCursor::operator== (StandardCursorType type) const noexcept
|
||||
{
|
||||
return cursorHandle != nullptr ? cursorHandle->isStandardType (type)
|
||||
: (type == NormalCursor);
|
||||
}
|
||||
|
||||
bool MouseCursor::operator!= (const MouseCursor& other) const noexcept { return ! operator== (other); }
|
||||
bool MouseCursor::operator!= (StandardCursorType type) const noexcept { return ! operator== (type); }
|
||||
|
||||
void* MouseCursor::getHandle() const noexcept
|
||||
{
|
||||
return cursorHandle != nullptr ? cursorHandle->getHandle() : nullptr;
|
||||
}
|
||||
|
||||
void MouseCursor::showWaitCursor()
|
||||
{
|
||||
Desktop::getInstance().getMainMouseSource().showMouseCursor (MouseCursor::WaitCursor);
|
||||
}
|
||||
|
||||
void MouseCursor::hideWaitCursor()
|
||||
{
|
||||
Desktop::getInstance().getMainMouseSource().revealCursor();
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_MOUSECURSOR_H_INCLUDED
|
||||
#define JUCE_MOUSECURSOR_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a mouse cursor image.
|
||||
|
||||
This object can either be used to represent one of the standard mouse
|
||||
cursor shapes, or a custom one generated from an image.
|
||||
*/
|
||||
class JUCE_API MouseCursor
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** The set of available standard mouse cursors. */
|
||||
enum StandardCursorType
|
||||
{
|
||||
ParentCursor = 0, /**< Indicates that the component's parent's cursor should be used. */
|
||||
|
||||
NoCursor, /**< An invisible cursor. */
|
||||
NormalCursor, /**< The stardard arrow cursor. */
|
||||
|
||||
WaitCursor, /**< The normal hourglass or spinning-beachball 'busy' cursor. */
|
||||
IBeamCursor, /**< A vertical I-beam for positioning within text. */
|
||||
CrosshairCursor, /**< A pair of crosshairs. */
|
||||
CopyingCursor, /**< The normal arrow cursor, but with a "+" on it to indicate
|
||||
that you're dragging a copy of something. */
|
||||
|
||||
PointingHandCursor, /**< A hand with a pointing finger, for clicking on web-links. */
|
||||
DraggingHandCursor, /**< An open flat hand for dragging heavy objects around. */
|
||||
|
||||
LeftRightResizeCursor, /**< An arrow pointing left and right. */
|
||||
UpDownResizeCursor, /**< an arrow pointing up and down. */
|
||||
UpDownLeftRightResizeCursor, /**< An arrow pointing up, down, left and right. */
|
||||
|
||||
TopEdgeResizeCursor, /**< A platform-specific cursor for resizing the top-edge of a window. */
|
||||
BottomEdgeResizeCursor, /**< A platform-specific cursor for resizing the bottom-edge of a window. */
|
||||
LeftEdgeResizeCursor, /**< A platform-specific cursor for resizing the left-edge of a window. */
|
||||
RightEdgeResizeCursor, /**< A platform-specific cursor for resizing the right-edge of a window. */
|
||||
TopLeftCornerResizeCursor, /**< A platform-specific cursor for resizing the top-left-corner of a window. */
|
||||
TopRightCornerResizeCursor, /**< A platform-specific cursor for resizing the top-right-corner of a window. */
|
||||
BottomLeftCornerResizeCursor, /**< A platform-specific cursor for resizing the bottom-left-corner of a window. */
|
||||
BottomRightCornerResizeCursor, /**< A platform-specific cursor for resizing the bottom-right-corner of a window. */
|
||||
|
||||
NumStandardCursorTypes
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates the standard arrow cursor. */
|
||||
MouseCursor() noexcept;
|
||||
|
||||
/** Creates one of the standard mouse cursor */
|
||||
MouseCursor (StandardCursorType);
|
||||
|
||||
/** Creates a custom cursor from an image.
|
||||
|
||||
@param image the image to use for the cursor - if this is bigger than the
|
||||
system can manage, it might get scaled down first, and might
|
||||
also have to be turned to black-and-white if it can't do colour
|
||||
cursors.
|
||||
@param hotSpotX the x position of the cursor's hotspot within the image
|
||||
@param hotSpotY the y position of the cursor's hotspot within the image
|
||||
*/
|
||||
MouseCursor (const Image& image, int hotSpotX, int hotSpotY);
|
||||
|
||||
/** Creates a custom cursor from an image.
|
||||
|
||||
@param image the image to use for the cursor - if this is bigger than the
|
||||
system can manage, it might get scaled down first, and might
|
||||
also have to be turned to black-and-white if it can't do colour
|
||||
cursors.
|
||||
@param hotSpotX the x position of the cursor's hotspot within the image
|
||||
@param hotSpotY the y position of the cursor's hotspot within the image
|
||||
@param scaleFactor the factor by which this image is larger than the target
|
||||
screen size of the cursor.
|
||||
*/
|
||||
MouseCursor (const Image& image, int hotSpotX, int hotSpotY, float scaleFactor);
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a copy of another cursor object. */
|
||||
MouseCursor (const MouseCursor&);
|
||||
|
||||
/** Copies this cursor from another object. */
|
||||
MouseCursor& operator= (const MouseCursor&);
|
||||
|
||||
/** Destructor. */
|
||||
~MouseCursor();
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
MouseCursor (MouseCursor&&) noexcept;
|
||||
MouseCursor& operator= (MouseCursor&&) noexcept;
|
||||
#endif
|
||||
|
||||
/** Checks whether two mouse cursors are the same.
|
||||
|
||||
For custom cursors, two cursors created from the same image won't be
|
||||
recognised as the same, only MouseCursor objects that have been
|
||||
copied from the same object.
|
||||
*/
|
||||
bool operator== (const MouseCursor&) const noexcept;
|
||||
|
||||
/** Checks whether two mouse cursors are the same.
|
||||
|
||||
For custom cursors, two cursors created from the same image won't be
|
||||
recognised as the same, only MouseCursor objects that have been
|
||||
copied from the same object.
|
||||
*/
|
||||
bool operator!= (const MouseCursor&) const noexcept;
|
||||
|
||||
/** Checks whether this cursor is of the standard type mentioned. */
|
||||
bool operator== (StandardCursorType type) const noexcept;
|
||||
|
||||
/** Checks whether this cursor is of the standard type mentioned. */
|
||||
bool operator!= (StandardCursorType type) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Makes the system show its default 'busy' cursor.
|
||||
|
||||
This will turn the system cursor to an hourglass or spinning beachball
|
||||
until the next time the mouse is moved, or hideWaitCursor() is called.
|
||||
|
||||
This is handy if the message loop is about to block for a couple of
|
||||
seconds while busy and you want to give the user feedback about this.
|
||||
|
||||
@see MessageManager::setTimeBeforeShowingWaitCursor
|
||||
*/
|
||||
static void showWaitCursor();
|
||||
|
||||
/** If showWaitCursor has been called, this will return the mouse to its
|
||||
normal state.
|
||||
|
||||
This will look at what component is under the mouse, and update the
|
||||
cursor to be the correct one for that component.
|
||||
|
||||
@see showWaitCursor
|
||||
*/
|
||||
static void hideWaitCursor();
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class SharedCursorHandle;
|
||||
friend class SharedCursorHandle;
|
||||
SharedCursorHandle* cursorHandle;
|
||||
|
||||
friend class MouseInputSourceInternal;
|
||||
void showInWindow (ComponentPeer* window) const;
|
||||
void showInAllWindows() const;
|
||||
void* getHandle() const noexcept;
|
||||
|
||||
static void* createStandardMouseCursor (MouseCursor::StandardCursorType type);
|
||||
static void deleteMouseCursor (void* cursorHandle, bool isStandard);
|
||||
|
||||
JUCE_LEAK_DETECTOR (MouseCursor)
|
||||
};
|
||||
|
||||
#endif // JUCE_MOUSECURSOR_H_INCLUDED
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
MouseEvent::MouseEvent (MouseInputSource inputSource,
|
||||
Point<float> pos,
|
||||
ModifierKeys modKeys,
|
||||
Component* const eventComp,
|
||||
Component* const originator,
|
||||
Time time,
|
||||
Point<float> downPos,
|
||||
Time downTime,
|
||||
const int numClicks,
|
||||
const bool mouseWasDragged) noexcept
|
||||
: position (pos),
|
||||
x (roundToInt (pos.x)),
|
||||
y (roundToInt (pos.y)),
|
||||
mods (modKeys),
|
||||
eventComponent (eventComp),
|
||||
originalComponent (originator),
|
||||
eventTime (time),
|
||||
mouseDownTime (downTime),
|
||||
source (inputSource),
|
||||
mouseDownPos (downPos),
|
||||
numberOfClicks ((uint8) numClicks),
|
||||
wasMovedSinceMouseDown ((uint8) (mouseWasDragged ? 1 : 0))
|
||||
{
|
||||
}
|
||||
|
||||
MouseEvent::~MouseEvent() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
MouseEvent MouseEvent::getEventRelativeTo (Component* const otherComponent) const noexcept
|
||||
{
|
||||
jassert (otherComponent != nullptr);
|
||||
|
||||
return MouseEvent (source, otherComponent->getLocalPoint (eventComponent, position),
|
||||
mods, otherComponent, originalComponent, eventTime,
|
||||
otherComponent->getLocalPoint (eventComponent, mouseDownPos),
|
||||
mouseDownTime, numberOfClicks, wasMovedSinceMouseDown != 0);
|
||||
}
|
||||
|
||||
MouseEvent MouseEvent::withNewPosition (Point<float> newPosition) const noexcept
|
||||
{
|
||||
return MouseEvent (source, newPosition, mods, eventComponent, originalComponent,
|
||||
eventTime, mouseDownPos, mouseDownTime,
|
||||
numberOfClicks, wasMovedSinceMouseDown != 0);
|
||||
}
|
||||
|
||||
MouseEvent MouseEvent::withNewPosition (Point<int> newPosition) const noexcept
|
||||
{
|
||||
return MouseEvent (source, newPosition.toFloat(), mods, eventComponent, originalComponent,
|
||||
eventTime, mouseDownPos, mouseDownTime,
|
||||
numberOfClicks, wasMovedSinceMouseDown != 0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool MouseEvent::mouseWasClicked() const noexcept
|
||||
{
|
||||
return wasMovedSinceMouseDown == 0;
|
||||
}
|
||||
|
||||
int MouseEvent::getLengthOfMousePress() const noexcept
|
||||
{
|
||||
if (mouseDownTime.toMilliseconds() > 0)
|
||||
return jmax (0, (int) (eventTime - mouseDownTime).inMilliseconds());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Point<int> MouseEvent::getPosition() const noexcept { return Point<int> (x, y); }
|
||||
Point<int> MouseEvent::getScreenPosition() const { return eventComponent->localPointToGlobal (getPosition()); }
|
||||
|
||||
Point<int> MouseEvent::getMouseDownPosition() const noexcept { return mouseDownPos.roundToInt(); }
|
||||
Point<int> MouseEvent::getMouseDownScreenPosition() const { return eventComponent->localPointToGlobal (mouseDownPos).roundToInt(); }
|
||||
|
||||
Point<int> MouseEvent::getOffsetFromDragStart() const noexcept { return (position - mouseDownPos).roundToInt(); }
|
||||
int MouseEvent::getDistanceFromDragStart() const noexcept { return roundToInt (mouseDownPos.getDistanceFrom (position)); }
|
||||
|
||||
int MouseEvent::getMouseDownX() const noexcept { return roundToInt (mouseDownPos.x); }
|
||||
int MouseEvent::getMouseDownY() const noexcept { return roundToInt (mouseDownPos.y); }
|
||||
|
||||
int MouseEvent::getDistanceFromDragStartX() const noexcept { return getOffsetFromDragStart().x; }
|
||||
int MouseEvent::getDistanceFromDragStartY() const noexcept { return getOffsetFromDragStart().y; }
|
||||
|
||||
int MouseEvent::getScreenX() const { return getScreenPosition().x; }
|
||||
int MouseEvent::getScreenY() const { return getScreenPosition().y; }
|
||||
|
||||
int MouseEvent::getMouseDownScreenX() const { return getMouseDownScreenPosition().x; }
|
||||
int MouseEvent::getMouseDownScreenY() const { return getMouseDownScreenPosition().y; }
|
||||
|
||||
//==============================================================================
|
||||
static int doubleClickTimeOutMs = 400;
|
||||
|
||||
int MouseEvent::getDoubleClickTimeout() noexcept { return doubleClickTimeOutMs; }
|
||||
void MouseEvent::setDoubleClickTimeout (const int newTime) noexcept { doubleClickTimeOutMs = newTime; }
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_MOUSEEVENT_H_INCLUDED
|
||||
#define JUCE_MOUSEEVENT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains position and status information about a mouse event.
|
||||
|
||||
@see MouseListener, Component::mouseMove, Component::mouseEnter, Component::mouseExit,
|
||||
Component::mouseDown, Component::mouseUp, Component::mouseDrag
|
||||
*/
|
||||
class JUCE_API MouseEvent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a MouseEvent.
|
||||
|
||||
Normally an application will never need to use this.
|
||||
|
||||
@param source the source that's invoking the event
|
||||
@param position the position of the mouse, relative to the component that is passed-in
|
||||
@param modifiers the key modifiers at the time of the event
|
||||
@param eventComponent the component that the mouse event applies to
|
||||
@param originator the component that originally received the event
|
||||
@param eventTime the time the event happened
|
||||
@param mouseDownPos the position of the corresponding mouse-down event (relative to the component that is passed-in).
|
||||
If there isn't a corresponding mouse-down (e.g. for a mouse-move), this will just be
|
||||
the same as the current mouse-x position.
|
||||
@param mouseDownTime the time at which the corresponding mouse-down event happened
|
||||
If there isn't a corresponding mouse-down (e.g. for a mouse-move), this will just be
|
||||
the same as the current mouse-event time.
|
||||
@param numberOfClicks how many clicks, e.g. a double-click event will be 2, a triple-click will be 3, etc
|
||||
@param mouseWasDragged whether the mouse has been dragged significantly since the previous mouse-down
|
||||
*/
|
||||
MouseEvent (MouseInputSource source,
|
||||
Point<float> position,
|
||||
ModifierKeys modifiers,
|
||||
Component* eventComponent,
|
||||
Component* originator,
|
||||
Time eventTime,
|
||||
Point<float> mouseDownPos,
|
||||
Time mouseDownTime,
|
||||
int numberOfClicks,
|
||||
bool mouseWasDragged) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~MouseEvent() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The position of the mouse when the event occurred.
|
||||
|
||||
This value is relative to the top-left of the component to which the
|
||||
event applies (as indicated by the MouseEvent::eventComponent field).
|
||||
|
||||
This is a more accurate floating-point version of the position returned by
|
||||
getPosition() and the integer x and y member variables.
|
||||
*/
|
||||
const Point<float> position;
|
||||
|
||||
/** The x-position of the mouse when the event occurred.
|
||||
|
||||
This value is relative to the top-left of the component to which the
|
||||
event applies (as indicated by the MouseEvent::eventComponent field).
|
||||
|
||||
For a floating-point coordinate, see MouseEvent::position
|
||||
*/
|
||||
const int x;
|
||||
|
||||
/** The y-position of the mouse when the event occurred.
|
||||
|
||||
This value is relative to the top-left of the component to which the
|
||||
event applies (as indicated by the MouseEvent::eventComponent field).
|
||||
|
||||
For a floating-point coordinate, see MouseEvent::position
|
||||
*/
|
||||
const int y;
|
||||
|
||||
/** The key modifiers associated with the event.
|
||||
|
||||
This will let you find out which mouse buttons were down, as well as which
|
||||
modifier keys were held down.
|
||||
|
||||
When used for mouse-up events, this will indicate the state of the mouse buttons
|
||||
just before they were released, so that you can tell which button they let go of.
|
||||
*/
|
||||
const ModifierKeys mods;
|
||||
|
||||
/** The component that this event applies to.
|
||||
|
||||
This is usually the component that the mouse was over at the time, but for mouse-drag
|
||||
events the mouse could actually be over a different component and the events are
|
||||
still sent to the component that the button was originally pressed on.
|
||||
|
||||
The x and y member variables are relative to this component's position.
|
||||
|
||||
If you use getEventRelativeTo() to retarget this object to be relative to a different
|
||||
component, this pointer will be updated, but originalComponent remains unchanged.
|
||||
|
||||
@see originalComponent
|
||||
*/
|
||||
Component* const eventComponent;
|
||||
|
||||
/** The component that the event first occurred on.
|
||||
|
||||
If you use getEventRelativeTo() to retarget this object to be relative to a different
|
||||
component, this value remains unchanged to indicate the first component that received it.
|
||||
|
||||
@see eventComponent
|
||||
*/
|
||||
Component* const originalComponent;
|
||||
|
||||
/** The time that this mouse-event occurred. */
|
||||
const Time eventTime;
|
||||
|
||||
/** The time that the corresponding mouse-down event occurred. */
|
||||
const Time mouseDownTime;
|
||||
|
||||
/** The source device that generated this event. */
|
||||
MouseInputSource source;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the x coordinate of the last place that a mouse was pressed.
|
||||
The coordinate is relative to the component specified in MouseEvent::component.
|
||||
@see getDistanceFromDragStart, getDistanceFromDragStartX, mouseWasClicked
|
||||
*/
|
||||
int getMouseDownX() const noexcept;
|
||||
|
||||
/** Returns the y coordinate of the last place that a mouse was pressed.
|
||||
The coordinate is relative to the component specified in MouseEvent::component.
|
||||
@see getDistanceFromDragStart, getDistanceFromDragStartX, mouseWasClicked
|
||||
*/
|
||||
int getMouseDownY() const noexcept;
|
||||
|
||||
/** Returns the coordinates of the last place that a mouse was pressed.
|
||||
The coordinates are relative to the component specified in MouseEvent::component.
|
||||
@see getDistanceFromDragStart, getDistanceFromDragStartX, mouseWasClicked
|
||||
*/
|
||||
Point<int> getMouseDownPosition() const noexcept;
|
||||
|
||||
/** Returns the straight-line distance between where the mouse is now and where it
|
||||
was the last time the button was pressed.
|
||||
|
||||
This is quite handy for things like deciding whether the user has moved far enough
|
||||
for it to be considered a drag operation.
|
||||
|
||||
@see getDistanceFromDragStartX
|
||||
*/
|
||||
int getDistanceFromDragStart() const noexcept;
|
||||
|
||||
/** Returns the difference between the mouse's current x postion and where it was
|
||||
when the button was last pressed.
|
||||
|
||||
@see getDistanceFromDragStart
|
||||
*/
|
||||
int getDistanceFromDragStartX() const noexcept;
|
||||
|
||||
/** Returns the difference between the mouse's current y postion and where it was
|
||||
when the button was last pressed.
|
||||
|
||||
@see getDistanceFromDragStart
|
||||
*/
|
||||
int getDistanceFromDragStartY() const noexcept;
|
||||
|
||||
/** Returns the difference between the mouse's current postion and where it was
|
||||
when the button was last pressed.
|
||||
|
||||
@see getDistanceFromDragStart
|
||||
*/
|
||||
Point<int> getOffsetFromDragStart() const noexcept;
|
||||
|
||||
/** Returns true if the mouse has just been clicked.
|
||||
|
||||
Used in either your mouseUp() or mouseDrag() methods, this will tell you whether
|
||||
the user has dragged the mouse more than a few pixels from the place where the
|
||||
mouse-down occurred.
|
||||
|
||||
Once they have dragged it far enough for this method to return false, it will continue
|
||||
to return false until the mouse-up, even if they move the mouse back to the same
|
||||
position where they originally pressed it. This means that it's very handy for
|
||||
objects that can either be clicked on or dragged, as you can use it in the mouseDrag()
|
||||
callback to ignore any small movements they might make while clicking.
|
||||
|
||||
@returns true if the mouse wasn't dragged by more than a few pixels between
|
||||
the last time the button was pressed and released.
|
||||
*/
|
||||
bool mouseWasClicked() const noexcept;
|
||||
|
||||
/** For a click event, the number of times the mouse was clicked in succession.
|
||||
|
||||
So for example a double-click event will return 2, a triple-click 3, etc.
|
||||
*/
|
||||
int getNumberOfClicks() const noexcept { return numberOfClicks; }
|
||||
|
||||
/** Returns the time that the mouse button has been held down for.
|
||||
|
||||
If called from a mouseDrag or mouseUp callback, this will return the
|
||||
number of milliseconds since the corresponding mouseDown event occurred.
|
||||
If called in other contexts, e.g. a mouseMove, then the returned value
|
||||
may be 0 or an undefined value.
|
||||
*/
|
||||
int getLengthOfMousePress() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The position of the mouse when the event occurred.
|
||||
|
||||
This position is relative to the top-left of the component to which the
|
||||
event applies (as indicated by the MouseEvent::eventComponent field).
|
||||
|
||||
For a floating-point position, see MouseEvent::position
|
||||
*/
|
||||
Point<int> getPosition() const noexcept;
|
||||
|
||||
/** Returns the mouse x position of this event, in global screen coordinates.
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
@see getScreenPosition
|
||||
*/
|
||||
int getScreenX() const;
|
||||
|
||||
/** Returns the mouse y position of this event, in global screen coordinates.
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
@see getScreenPosition
|
||||
*/
|
||||
int getScreenY() const;
|
||||
|
||||
/** Returns the mouse position of this event, in global screen coordinates.
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
@see getMouseDownScreenPosition
|
||||
*/
|
||||
Point<int> getScreenPosition() const;
|
||||
|
||||
/** Returns the x coordinate at which the mouse button was last pressed.
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
@see getMouseDownScreenPosition
|
||||
*/
|
||||
int getMouseDownScreenX() const;
|
||||
|
||||
/** Returns the y coordinate at which the mouse button was last pressed.
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
@see getMouseDownScreenPosition
|
||||
*/
|
||||
int getMouseDownScreenY() const;
|
||||
|
||||
/** Returns the coordinates at which the mouse button was last pressed.
|
||||
The coordinates are relative to the top-left of the main monitor.
|
||||
@see getScreenPosition
|
||||
*/
|
||||
Point<int> getMouseDownScreenPosition() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a version of this event that is relative to a different component.
|
||||
|
||||
The x and y positions of the event that is returned will have been
|
||||
adjusted to be relative to the new component.
|
||||
The component pointer that is passed-in must not be null.
|
||||
*/
|
||||
MouseEvent getEventRelativeTo (Component* newComponent) const noexcept;
|
||||
|
||||
/** Creates a copy of this event with a different position.
|
||||
All other members of the event object are the same, but the x and y are
|
||||
replaced with these new values.
|
||||
*/
|
||||
MouseEvent withNewPosition (Point<float> newPosition) const noexcept;
|
||||
|
||||
/** Creates a copy of this event with a different position.
|
||||
All other members of the event object are the same, but the x and y are
|
||||
replaced with these new values.
|
||||
*/
|
||||
MouseEvent withNewPosition (Point<int> newPosition) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the application-wide setting for the double-click time limit.
|
||||
|
||||
This is the maximum length of time between mouse-clicks for it to be
|
||||
considered a double-click. It's used by the Component class.
|
||||
|
||||
@see getDoubleClickTimeout, MouseListener::mouseDoubleClick
|
||||
*/
|
||||
static void setDoubleClickTimeout (int timeOutMilliseconds) noexcept;
|
||||
|
||||
/** Returns the application-wide setting for the double-click time limit.
|
||||
|
||||
This is the maximum length of time between mouse-clicks for it to be
|
||||
considered a double-click. It's used by the Component class.
|
||||
|
||||
@see setDoubleClickTimeout, MouseListener::mouseDoubleClick
|
||||
*/
|
||||
static int getDoubleClickTimeout() noexcept;
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const Point<float> mouseDownPos;
|
||||
const uint8 numberOfClicks, wasMovedSinceMouseDown;
|
||||
|
||||
MouseEvent& operator= (const MouseEvent&);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains status information about a mouse wheel event.
|
||||
|
||||
@see MouseListener, MouseEvent
|
||||
*/
|
||||
struct MouseWheelDetails
|
||||
{
|
||||
//==============================================================================
|
||||
/** The amount that the wheel has been moved in the X axis.
|
||||
|
||||
If isReversed is true, then a negative deltaX means that the wheel has been
|
||||
pushed physically to the left.
|
||||
If isReversed is false, then a negative deltaX means that the wheel has been
|
||||
pushed physically to the right.
|
||||
*/
|
||||
float deltaX;
|
||||
|
||||
/** The amount that the wheel has been moved in the Y axis.
|
||||
|
||||
If isReversed is true, then a negative deltaY means that the wheel has been
|
||||
pushed physically upwards.
|
||||
If isReversed is false, then a negative deltaY means that the wheel has been
|
||||
pushed physically downwards.
|
||||
*/
|
||||
float deltaY;
|
||||
|
||||
/** Indicates whether the user has reversed the direction of the wheel.
|
||||
See deltaX and deltaY for an explanation of the effects of this value.
|
||||
*/
|
||||
bool isReversed;
|
||||
|
||||
/** If true, then the wheel has continuous, un-stepped motion. */
|
||||
bool isSmooth;
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_MOUSEEVENT_H_INCLUDED
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
MouseInactivityDetector::MouseInactivityDetector (Component& c)
|
||||
: targetComp (c), delayMs (1500), isActive (true)
|
||||
{
|
||||
targetComp.addMouseListener (this, true);
|
||||
}
|
||||
|
||||
MouseInactivityDetector::~MouseInactivityDetector()
|
||||
{
|
||||
targetComp.removeMouseListener (this);
|
||||
}
|
||||
|
||||
void MouseInactivityDetector::setDelay (int newDelayMilliseconds)
|
||||
{
|
||||
delayMs = newDelayMilliseconds;
|
||||
}
|
||||
|
||||
|
||||
void MouseInactivityDetector::addListener (Listener* l) { listenerList.add (l); }
|
||||
void MouseInactivityDetector::removeListener (Listener* l) { listenerList.remove (l); }
|
||||
|
||||
void MouseInactivityDetector::timerCallback()
|
||||
{
|
||||
setActive (false);
|
||||
}
|
||||
|
||||
void MouseInactivityDetector::wakeUp (const MouseEvent& e, bool alwaysWake)
|
||||
{
|
||||
const Point<int> newPos (e.getEventRelativeTo (&targetComp).getPosition());
|
||||
|
||||
if ((! isActive) && (alwaysWake || e.source.isTouch() || newPos.getDistanceFrom (lastMousePos) > 15))
|
||||
setActive (true);
|
||||
|
||||
if (lastMousePos != newPos)
|
||||
{
|
||||
lastMousePos = newPos;
|
||||
startTimer (delayMs);
|
||||
}
|
||||
}
|
||||
|
||||
void MouseInactivityDetector::setActive (bool b)
|
||||
{
|
||||
if (isActive != b)
|
||||
{
|
||||
isActive = b;
|
||||
|
||||
listenerList.call (b ? &Listener::mouseBecameActive
|
||||
: &Listener::mouseBecameInactive);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_MOUSEINACTIVITYDETECTOR_H_INCLUDED
|
||||
#define JUCE_MOUSEINACTIVITYDETECTOR_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This object watches for mouse-events happening within a component, and if
|
||||
the mouse remains still for long enough, triggers an event to indicate that
|
||||
it has become inactive.
|
||||
|
||||
You'd use this for situations where e.g. you want to hide the mouse-cursor
|
||||
when the user's not actively using the mouse.
|
||||
|
||||
After creating an instance of this, use addListener to get callbacks when
|
||||
the activity status changes.
|
||||
*/
|
||||
class JUCE_API MouseInactivityDetector : private Timer,
|
||||
private MouseListener
|
||||
{
|
||||
public:
|
||||
/** Creates an inactivity watcher, attached to the given component.
|
||||
The target component must not be deleted while this - it will be monitored
|
||||
for any mouse events in it or its child components.
|
||||
*/
|
||||
MouseInactivityDetector (Component& target);
|
||||
|
||||
/** Destructor. */
|
||||
~MouseInactivityDetector();
|
||||
|
||||
/** Sets the time for which the mouse must be still before the callback
|
||||
is triggered.
|
||||
*/
|
||||
void setDelay (int newDelayMilliseconds);
|
||||
|
||||
//==============================================================================
|
||||
/** Classes should implement this to receive callbacks from a MouseInactivityDetector
|
||||
when the mouse becomes active or inactive.
|
||||
*/
|
||||
class Listener
|
||||
{
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
|
||||
/** Called when the mouse is moved or clicked for the first time
|
||||
after a period of inactivity. */
|
||||
virtual void mouseBecameActive() = 0;
|
||||
|
||||
/** Called when the mouse hasn't been moved for the timeout period. */
|
||||
virtual void mouseBecameInactive() = 0;
|
||||
};
|
||||
|
||||
/** Registers a listener. */
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Removes a previously-registered listener. */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Component& targetComp;
|
||||
ListenerList<Listener> listenerList;
|
||||
Point<int> lastMousePos;
|
||||
int delayMs;
|
||||
bool isActive;
|
||||
|
||||
void timerCallback() override;
|
||||
void wakeUp (const MouseEvent&, bool alwaysWake);
|
||||
void setActive (bool);
|
||||
|
||||
void mouseMove (const MouseEvent& e) override { wakeUp (e, false); }
|
||||
void mouseEnter (const MouseEvent& e) override { wakeUp (e, false); }
|
||||
void mouseExit (const MouseEvent& e) override { wakeUp (e, false); }
|
||||
void mouseDown (const MouseEvent& e) override { wakeUp (e, true); }
|
||||
void mouseDrag (const MouseEvent& e) override { wakeUp (e, true); }
|
||||
void mouseUp (const MouseEvent& e) override { wakeUp (e, true); }
|
||||
void mouseWheelMove (const MouseEvent& e, const MouseWheelDetails&) override { wakeUp (e, true); }
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInactivityDetector)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_MOUSEINACTIVITYDETECTOR_H_INCLUDED
|
||||
|
|
@ -0,0 +1,685 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class MouseInputSourceInternal : private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MouseInputSourceInternal (const int i, const bool isMouse)
|
||||
: index (i), isMouseDevice (isMouse),
|
||||
isUnboundedMouseModeOn (false), isCursorVisibleUntilOffscreen (false),
|
||||
lastPeer (nullptr), currentCursorHandle (nullptr),
|
||||
mouseEventCounter (0), mouseMovedSignificantlySincePressed (false)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isDragging() const noexcept
|
||||
{
|
||||
return buttonState.isAnyMouseButtonDown();
|
||||
}
|
||||
|
||||
Component* getComponentUnderMouse() const
|
||||
{
|
||||
return componentUnderMouse.get();
|
||||
}
|
||||
|
||||
ModifierKeys getCurrentModifiers() const
|
||||
{
|
||||
return ModifierKeys::getCurrentModifiers().withoutMouseButtons().withFlags (buttonState.getRawFlags());
|
||||
}
|
||||
|
||||
ComponentPeer* getPeer()
|
||||
{
|
||||
if (! ComponentPeer::isValidPeer (lastPeer))
|
||||
lastPeer = nullptr;
|
||||
|
||||
return lastPeer;
|
||||
}
|
||||
|
||||
static Point<float> screenPosToLocalPos (Component& comp, Point<float> pos)
|
||||
{
|
||||
if (ComponentPeer* const peer = comp.getPeer())
|
||||
{
|
||||
pos = peer->globalToLocal (pos);
|
||||
Component& peerComp = peer->getComponent();
|
||||
return comp.getLocalPoint (&peerComp, ScalingHelpers::unscaledScreenPosToScaled (peerComp, pos));
|
||||
}
|
||||
|
||||
return comp.getLocalPoint (nullptr, ScalingHelpers::unscaledScreenPosToScaled (comp, pos));
|
||||
}
|
||||
|
||||
Component* findComponentAt (Point<float> screenPos)
|
||||
{
|
||||
if (ComponentPeer* const peer = getPeer())
|
||||
{
|
||||
Point<float> relativePos (ScalingHelpers::unscaledScreenPosToScaled (peer->getComponent(),
|
||||
peer->globalToLocal (screenPos)));
|
||||
Component& comp = peer->getComponent();
|
||||
|
||||
const Point<int> pos (relativePos.roundToInt());
|
||||
|
||||
// (the contains() call is needed to test for overlapping desktop windows)
|
||||
if (comp.contains (pos))
|
||||
return comp.getComponentAt (pos);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Point<float> getScreenPosition() const
|
||||
{
|
||||
// This needs to return the live position if possible, but it mustn't update the lastScreenPos
|
||||
// value, because that can cause continuity problems.
|
||||
return ScalingHelpers::unscaledScreenPosToScaled
|
||||
(unboundedMouseOffset + (isMouseDevice ? MouseInputSource::getCurrentRawMousePosition()
|
||||
: lastScreenPos));
|
||||
}
|
||||
|
||||
void setScreenPosition (Point<float> p)
|
||||
{
|
||||
MouseInputSource::setRawMousePosition (ScalingHelpers::scaledScreenPosToUnscaled (p));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_DUMP_MOUSE_EVENTS
|
||||
#define JUCE_MOUSE_EVENT_DBG(desc) DBG ("Mouse " << desc << " #" << index \
|
||||
<< ": " << screenPosToLocalPos (comp, screenPos).toString() \
|
||||
<< " - Comp: " << String::toHexString ((pointer_sized_int) &comp));
|
||||
#else
|
||||
#define JUCE_MOUSE_EVENT_DBG(desc)
|
||||
#endif
|
||||
|
||||
void sendMouseEnter (Component& comp, Point<float> screenPos, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("enter")
|
||||
comp.internalMouseEnter (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
|
||||
}
|
||||
|
||||
void sendMouseExit (Component& comp, Point<float> screenPos, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("exit")
|
||||
comp.internalMouseExit (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
|
||||
}
|
||||
|
||||
void sendMouseMove (Component& comp, Point<float> screenPos, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("move")
|
||||
comp.internalMouseMove (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
|
||||
}
|
||||
|
||||
void sendMouseDown (Component& comp, Point<float> screenPos, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("down")
|
||||
comp.internalMouseDown (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
|
||||
}
|
||||
|
||||
void sendMouseDrag (Component& comp, Point<float> screenPos, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("drag")
|
||||
comp.internalMouseDrag (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
|
||||
}
|
||||
|
||||
void sendMouseUp (Component& comp, Point<float> screenPos, Time time, const ModifierKeys oldMods)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("up")
|
||||
comp.internalMouseUp (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, oldMods);
|
||||
}
|
||||
|
||||
void sendMouseWheel (Component& comp, Point<float> screenPos, Time time, const MouseWheelDetails& wheel)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("wheel")
|
||||
comp.internalMouseWheel (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, wheel);
|
||||
}
|
||||
|
||||
void sendMagnifyGesture (Component& comp, Point<float> screenPos, Time time, const float amount)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("magnify")
|
||||
comp.internalMagnifyGesture (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, amount);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// (returns true if the button change caused a modal event loop)
|
||||
bool setButtons (Point<float> screenPos, Time time, const ModifierKeys newButtonState)
|
||||
{
|
||||
if (buttonState == newButtonState)
|
||||
return false;
|
||||
|
||||
// (avoid sending a spurious mouse-drag when we receive a mouse-up)
|
||||
if (! (isDragging() && ! newButtonState.isAnyMouseButtonDown()))
|
||||
setScreenPos (screenPos, time, false);
|
||||
|
||||
// (ignore secondary clicks when there's already a button down)
|
||||
if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
|
||||
{
|
||||
buttonState = newButtonState;
|
||||
return false;
|
||||
}
|
||||
|
||||
const int lastCounter = mouseEventCounter;
|
||||
|
||||
if (buttonState.isAnyMouseButtonDown())
|
||||
{
|
||||
if (Component* const current = getComponentUnderMouse())
|
||||
{
|
||||
const ModifierKeys oldMods (getCurrentModifiers());
|
||||
buttonState = newButtonState; // must change this before calling sendMouseUp, in case it runs a modal loop
|
||||
|
||||
sendMouseUp (*current, screenPos + unboundedMouseOffset, time, oldMods);
|
||||
|
||||
if (lastCounter != mouseEventCounter)
|
||||
return true; // if a modal loop happened, then newButtonState is no longer valid.
|
||||
}
|
||||
|
||||
enableUnboundedMouseMovement (false, false);
|
||||
}
|
||||
|
||||
buttonState = newButtonState;
|
||||
|
||||
if (buttonState.isAnyMouseButtonDown())
|
||||
{
|
||||
Desktop::getInstance().incrementMouseClickCounter();
|
||||
|
||||
if (Component* const current = getComponentUnderMouse())
|
||||
{
|
||||
registerMouseDown (screenPos, time, *current, buttonState);
|
||||
sendMouseDown (*current, screenPos, time);
|
||||
}
|
||||
}
|
||||
|
||||
return lastCounter != mouseEventCounter;
|
||||
}
|
||||
|
||||
void setComponentUnderMouse (Component* const newComponent, Point<float> screenPos, Time time)
|
||||
{
|
||||
Component* current = getComponentUnderMouse();
|
||||
|
||||
if (newComponent != current)
|
||||
{
|
||||
WeakReference<Component> safeNewComp (newComponent);
|
||||
const ModifierKeys originalButtonState (buttonState);
|
||||
|
||||
if (current != nullptr)
|
||||
{
|
||||
WeakReference<Component> safeOldComp (current);
|
||||
setButtons (screenPos, time, ModifierKeys());
|
||||
|
||||
if (safeOldComp != nullptr)
|
||||
{
|
||||
componentUnderMouse = safeNewComp;
|
||||
sendMouseExit (*safeOldComp, screenPos, time);
|
||||
}
|
||||
|
||||
buttonState = originalButtonState;
|
||||
}
|
||||
|
||||
current = componentUnderMouse = safeNewComp;
|
||||
|
||||
if (current != nullptr)
|
||||
sendMouseEnter (*current, screenPos, time);
|
||||
|
||||
revealCursor (false);
|
||||
setButtons (screenPos, time, originalButtonState);
|
||||
}
|
||||
}
|
||||
|
||||
void setPeer (ComponentPeer& newPeer, Point<float> screenPos, Time time)
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
|
||||
if (&newPeer != lastPeer)
|
||||
{
|
||||
setComponentUnderMouse (nullptr, screenPos, time);
|
||||
lastPeer = &newPeer;
|
||||
setComponentUnderMouse (findComponentAt (screenPos), screenPos, time);
|
||||
}
|
||||
}
|
||||
|
||||
void setScreenPos (Point<float> newScreenPos, Time time, const bool forceUpdate)
|
||||
{
|
||||
if (! isDragging())
|
||||
setComponentUnderMouse (findComponentAt (newScreenPos), newScreenPos, time);
|
||||
|
||||
if (newScreenPos != lastScreenPos || forceUpdate)
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
lastScreenPos = newScreenPos;
|
||||
|
||||
if (Component* const current = getComponentUnderMouse())
|
||||
{
|
||||
if (isDragging())
|
||||
{
|
||||
registerMouseDrag (newScreenPos);
|
||||
sendMouseDrag (*current, newScreenPos + unboundedMouseOffset, time);
|
||||
|
||||
if (isUnboundedMouseModeOn)
|
||||
handleUnboundedDrag (*current);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendMouseMove (*current, newScreenPos, time);
|
||||
}
|
||||
}
|
||||
|
||||
revealCursor (false);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleEvent (ComponentPeer& newPeer, Point<float> positionWithinPeer, Time time, const ModifierKeys newMods)
|
||||
{
|
||||
lastTime = time;
|
||||
++mouseEventCounter;
|
||||
const Point<float> screenPos (newPeer.localToGlobal (positionWithinPeer));
|
||||
|
||||
if (isDragging() && newMods.isAnyMouseButtonDown())
|
||||
{
|
||||
setScreenPos (screenPos, time, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
setPeer (newPeer, screenPos, time);
|
||||
|
||||
if (ComponentPeer* peer = getPeer())
|
||||
{
|
||||
if (setButtons (screenPos, time, newMods))
|
||||
return; // some modal events have been dispatched, so the current event is now out-of-date
|
||||
|
||||
peer = getPeer();
|
||||
if (peer != nullptr)
|
||||
setScreenPos (screenPos, time, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component* getTargetForGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
|
||||
Time time, Point<float>& screenPos)
|
||||
{
|
||||
lastTime = time;
|
||||
++mouseEventCounter;
|
||||
|
||||
screenPos = peer.localToGlobal (positionWithinPeer);
|
||||
setPeer (peer, screenPos, time);
|
||||
setScreenPos (screenPos, time, false);
|
||||
triggerFakeMove();
|
||||
|
||||
return isDragging() ? nullptr : getComponentUnderMouse();
|
||||
}
|
||||
|
||||
void handleWheel (ComponentPeer& peer, Point<float> positionWithinPeer,
|
||||
Time time, const MouseWheelDetails& wheel)
|
||||
{
|
||||
Desktop::getInstance().incrementMouseWheelCounter();
|
||||
|
||||
Point<float> screenPos;
|
||||
if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
|
||||
sendMouseWheel (*current, screenPos, time, wheel);
|
||||
}
|
||||
|
||||
void handleMagnifyGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
|
||||
Time time, const float scaleFactor)
|
||||
{
|
||||
Point<float> screenPos;
|
||||
if (Component* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
|
||||
sendMagnifyGesture (*current, screenPos, time, scaleFactor);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Time getLastMouseDownTime() const noexcept { return mouseDowns[0].time; }
|
||||
Point<float> getLastMouseDownPosition() const noexcept { return ScalingHelpers::unscaledScreenPosToScaled (mouseDowns[0].position); }
|
||||
|
||||
int getNumberOfMultipleClicks() const noexcept
|
||||
{
|
||||
int numClicks = 0;
|
||||
|
||||
if (mouseDowns[0].time != Time())
|
||||
{
|
||||
if (! mouseMovedSignificantlySincePressed)
|
||||
++numClicks;
|
||||
|
||||
for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
|
||||
{
|
||||
if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
|
||||
++numClicks;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return numClicks;
|
||||
}
|
||||
|
||||
bool hasMouseMovedSignificantlySincePressed() const noexcept
|
||||
{
|
||||
return mouseMovedSignificantlySincePressed
|
||||
|| lastTime > mouseDowns[0].time + RelativeTime::milliseconds (300);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void triggerFakeMove()
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
setScreenPos (lastScreenPos, jmax (lastTime, Time::getCurrentTime()), true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
|
||||
{
|
||||
enable = enable && isDragging();
|
||||
isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
|
||||
|
||||
if (enable != isUnboundedMouseModeOn)
|
||||
{
|
||||
if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
|
||||
{
|
||||
// when released, return the mouse to within the component's bounds
|
||||
if (Component* current = getComponentUnderMouse())
|
||||
setScreenPosition (current->getScreenBounds().toFloat()
|
||||
.getConstrainedPoint (ScalingHelpers::unscaledScreenPosToScaled (lastScreenPos)));
|
||||
}
|
||||
|
||||
isUnboundedMouseModeOn = enable;
|
||||
unboundedMouseOffset = Point<float>();
|
||||
|
||||
revealCursor (true);
|
||||
}
|
||||
}
|
||||
|
||||
void handleUnboundedDrag (Component& current)
|
||||
{
|
||||
const Rectangle<float> componentScreenBounds
|
||||
= ScalingHelpers::scaledScreenPosToUnscaled (current.getParentMonitorArea().reduced (2, 2).toFloat());
|
||||
|
||||
if (! componentScreenBounds.contains (lastScreenPos))
|
||||
{
|
||||
const Point<float> componentCentre (current.getScreenBounds().toFloat().getCentre());
|
||||
unboundedMouseOffset += (lastScreenPos - ScalingHelpers::scaledScreenPosToUnscaled (componentCentre));
|
||||
setScreenPosition (componentCentre);
|
||||
}
|
||||
else if (isCursorVisibleUntilOffscreen
|
||||
&& (! unboundedMouseOffset.isOrigin())
|
||||
&& componentScreenBounds.contains (lastScreenPos + unboundedMouseOffset))
|
||||
{
|
||||
MouseInputSource::setRawMousePosition (lastScreenPos + unboundedMouseOffset);
|
||||
unboundedMouseOffset = Point<float>();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
|
||||
{
|
||||
if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
|
||||
{
|
||||
cursor = MouseCursor::NoCursor;
|
||||
forcedUpdate = true;
|
||||
}
|
||||
|
||||
if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
|
||||
{
|
||||
currentCursorHandle = cursor.getHandle();
|
||||
cursor.showInWindow (getPeer());
|
||||
}
|
||||
}
|
||||
|
||||
void hideCursor()
|
||||
{
|
||||
showMouseCursor (MouseCursor::NoCursor, true);
|
||||
}
|
||||
|
||||
void revealCursor (bool forcedUpdate)
|
||||
{
|
||||
MouseCursor mc (MouseCursor::NormalCursor);
|
||||
|
||||
if (Component* current = getComponentUnderMouse())
|
||||
mc = current->getLookAndFeel().getMouseCursorFor (*current);
|
||||
|
||||
showMouseCursor (mc, forcedUpdate);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const int index;
|
||||
const bool isMouseDevice;
|
||||
Point<float> lastScreenPos, unboundedMouseOffset; // NB: these are unscaled coords
|
||||
ModifierKeys buttonState;
|
||||
|
||||
bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
|
||||
|
||||
private:
|
||||
WeakReference<Component> componentUnderMouse;
|
||||
ComponentPeer* lastPeer;
|
||||
|
||||
void* currentCursorHandle;
|
||||
int mouseEventCounter;
|
||||
|
||||
struct RecentMouseDown
|
||||
{
|
||||
RecentMouseDown() noexcept : peerID (0) {}
|
||||
|
||||
Point<float> position;
|
||||
Time time;
|
||||
ModifierKeys buttons;
|
||||
uint32 peerID;
|
||||
|
||||
bool canBePartOfMultipleClickWith (const RecentMouseDown& other, const int maxTimeBetweenMs) const
|
||||
{
|
||||
return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
|
||||
&& std::abs (position.x - other.position.x) < 8
|
||||
&& std::abs (position.y - other.position.y) < 8
|
||||
&& buttons == other.buttons
|
||||
&& peerID == other.peerID;
|
||||
}
|
||||
};
|
||||
|
||||
RecentMouseDown mouseDowns[4];
|
||||
Time lastTime;
|
||||
bool mouseMovedSignificantlySincePressed;
|
||||
|
||||
void registerMouseDown (Point<float> screenPos, Time time,
|
||||
Component& component, const ModifierKeys modifiers) noexcept
|
||||
{
|
||||
for (int i = numElementsInArray (mouseDowns); --i > 0;)
|
||||
mouseDowns[i] = mouseDowns[i - 1];
|
||||
|
||||
mouseDowns[0].position = screenPos;
|
||||
mouseDowns[0].time = time;
|
||||
mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
|
||||
|
||||
if (ComponentPeer* const peer = component.getPeer())
|
||||
mouseDowns[0].peerID = peer->getUniqueID();
|
||||
else
|
||||
mouseDowns[0].peerID = 0;
|
||||
|
||||
mouseMovedSignificantlySincePressed = false;
|
||||
}
|
||||
|
||||
void registerMouseDrag (Point<float> screenPos) noexcept
|
||||
{
|
||||
mouseMovedSignificantlySincePressed = mouseMovedSignificantlySincePressed
|
||||
|| mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceInternal)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
MouseInputSource::MouseInputSource (MouseInputSourceInternal* s) noexcept : pimpl (s) {}
|
||||
MouseInputSource::MouseInputSource (const MouseInputSource& other) noexcept : pimpl (other.pimpl) {}
|
||||
MouseInputSource::~MouseInputSource() noexcept {}
|
||||
|
||||
MouseInputSource& MouseInputSource::operator= (const MouseInputSource& other) noexcept
|
||||
{
|
||||
pimpl = other.pimpl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool MouseInputSource::isMouse() const { return pimpl->isMouseDevice; }
|
||||
bool MouseInputSource::isTouch() const { return ! isMouse(); }
|
||||
bool MouseInputSource::canHover() const { return isMouse(); }
|
||||
bool MouseInputSource::hasMouseWheel() const { return isMouse(); }
|
||||
int MouseInputSource::getIndex() const { return pimpl->index; }
|
||||
bool MouseInputSource::isDragging() const { return pimpl->isDragging(); }
|
||||
Point<float> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
|
||||
ModifierKeys MouseInputSource::getCurrentModifiers() const { return pimpl->getCurrentModifiers(); }
|
||||
Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
|
||||
void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
|
||||
int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
|
||||
Time MouseInputSource::getLastMouseDownTime() const noexcept { return pimpl->getLastMouseDownTime(); }
|
||||
Point<float> MouseInputSource::getLastMouseDownPosition() const noexcept { return pimpl->getLastMouseDownPosition(); }
|
||||
bool MouseInputSource::hasMouseMovedSignificantlySincePressed() const noexcept { return pimpl->hasMouseMovedSignificantlySincePressed(); }
|
||||
bool MouseInputSource::canDoUnboundedMovement() const noexcept { return isMouse(); }
|
||||
void MouseInputSource::enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen) const
|
||||
{ pimpl->enableUnboundedMouseMovement (isEnabled, keepCursorVisibleUntilOffscreen); }
|
||||
bool MouseInputSource::isUnboundedMouseMovementEnabled() const { return pimpl->isUnboundedMouseModeOn; }
|
||||
bool MouseInputSource::hasMouseCursor() const noexcept { return isMouse(); }
|
||||
void MouseInputSource::showMouseCursor (const MouseCursor& cursor) { pimpl->showMouseCursor (cursor, false); }
|
||||
void MouseInputSource::hideCursor() { pimpl->hideCursor(); }
|
||||
void MouseInputSource::revealCursor() { pimpl->revealCursor (false); }
|
||||
void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
|
||||
void MouseInputSource::setScreenPosition (Point<float> p) { pimpl->setScreenPosition (p); }
|
||||
|
||||
void MouseInputSource::handleEvent (ComponentPeer& peer, Point<float> pos, int64 time, ModifierKeys mods)
|
||||
{
|
||||
pimpl->handleEvent (peer, pos, Time (time), mods.withOnlyMouseButtons());
|
||||
}
|
||||
|
||||
void MouseInputSource::handleWheel (ComponentPeer& peer, Point<float> pos, int64 time, const MouseWheelDetails& wheel)
|
||||
{
|
||||
pimpl->handleWheel (peer, pos, Time (time), wheel);
|
||||
}
|
||||
|
||||
void MouseInputSource::handleMagnifyGesture (ComponentPeer& peer, Point<float> pos, int64 time, float scaleFactor)
|
||||
{
|
||||
pimpl->handleMagnifyGesture (peer, pos, Time (time), scaleFactor);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct MouseInputSource::SourceList : public Timer
|
||||
{
|
||||
SourceList()
|
||||
{
|
||||
addSource();
|
||||
}
|
||||
|
||||
bool addSource();
|
||||
|
||||
void addSource (int index, bool isMouse)
|
||||
{
|
||||
MouseInputSourceInternal* s = new MouseInputSourceInternal (index, isMouse);
|
||||
sources.add (s);
|
||||
sourceArray.add (MouseInputSource (s));
|
||||
}
|
||||
|
||||
MouseInputSource* getMouseSource (int index) const noexcept
|
||||
{
|
||||
return isPositiveAndBelow (index, sourceArray.size()) ? &sourceArray.getReference (index)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
MouseInputSource* getOrCreateMouseInputSource (int touchIndex)
|
||||
{
|
||||
jassert (touchIndex >= 0 && touchIndex < 100); // sanity-check on number of fingers
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (MouseInputSource* mouse = getMouseSource (touchIndex))
|
||||
return mouse;
|
||||
|
||||
if (! addSource())
|
||||
{
|
||||
jassertfalse; // not enough mouse sources!
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getNumDraggingMouseSources() const noexcept
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
for (int i = 0; i < sources.size(); ++i)
|
||||
if (sources.getUnchecked(i)->isDragging())
|
||||
++num;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
MouseInputSource* getDraggingMouseSource (int index) const noexcept
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
for (int i = 0; i < sources.size(); ++i)
|
||||
{
|
||||
MouseInputSource* const mi = &(sourceArray.getReference(i));
|
||||
|
||||
if (mi->isDragging())
|
||||
{
|
||||
if (index == num)
|
||||
return mi;
|
||||
|
||||
++num;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void beginDragAutoRepeat (const int interval)
|
||||
{
|
||||
if (interval > 0)
|
||||
{
|
||||
if (getTimerInterval() != interval)
|
||||
startTimer (interval);
|
||||
}
|
||||
else
|
||||
{
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
int numMiceDown = 0;
|
||||
|
||||
for (int i = 0; i < sources.size(); ++i)
|
||||
{
|
||||
MouseInputSourceInternal* const mi = sources.getUnchecked(i);
|
||||
|
||||
if (mi->isDragging())
|
||||
{
|
||||
mi->triggerFakeMove();
|
||||
++numMiceDown;
|
||||
}
|
||||
}
|
||||
|
||||
if (numMiceDown == 0)
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
OwnedArray<MouseInputSourceInternal> sources;
|
||||
Array<MouseInputSource> sourceArray;
|
||||
};
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_MOUSEINPUTSOURCE_H_INCLUDED
|
||||
#define JUCE_MOUSEINPUTSOURCE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a linear source of mouse events from a mouse device or individual finger
|
||||
in a multi-touch environment.
|
||||
|
||||
Each MouseEvent object contains a reference to the MouseInputSource that generated
|
||||
it. In an environment with a single mouse for input, all events will come from the
|
||||
same source, but in a multi-touch system, there may be multiple MouseInputSource
|
||||
obects active, each representing a stream of events coming from a particular finger.
|
||||
|
||||
Events coming from a single MouseInputSource are always sent in a fixed and predictable
|
||||
order: a mouseMove will never be called without a mouseEnter having been sent beforehand,
|
||||
the only events that can happen between a mouseDown and its corresponding mouseUp are
|
||||
mouseDrags, etc.
|
||||
When there are multiple touches arriving from multiple MouseInputSources, their
|
||||
event streams may arrive in an interleaved order, so you should use the getIndex()
|
||||
method to find out which finger each event came from.
|
||||
|
||||
@see MouseEvent
|
||||
*/
|
||||
class JUCE_API MouseInputSource
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MouseInputSource (const MouseInputSource&) noexcept;
|
||||
MouseInputSource& operator= (const MouseInputSource&) noexcept;
|
||||
~MouseInputSource() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
bool operator== (const MouseInputSource& other) const noexcept { return pimpl == other.pimpl; }
|
||||
bool operator!= (const MouseInputSource& other) const noexcept { return pimpl != other.pimpl; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if this object represents a normal desk-based mouse device. */
|
||||
bool isMouse() const;
|
||||
|
||||
/** Returns true if this object represents a source of touch events - i.e. a finger or stylus. */
|
||||
bool isTouch() const;
|
||||
|
||||
/** Returns true if this source has an on-screen pointer that can hover over
|
||||
items without clicking them.
|
||||
*/
|
||||
bool canHover() const;
|
||||
|
||||
/** Returns true if this source may have a scroll wheel. */
|
||||
bool hasMouseWheel() const;
|
||||
|
||||
/** Returns this source's index in the global list of possible sources.
|
||||
If the system only has a single mouse, there will only be a single MouseInputSource
|
||||
with an index of 0.
|
||||
|
||||
If the system supports multi-touch input, then the index will represent a finger
|
||||
number, starting from 0. When the first touch event begins, it will have finger
|
||||
number 0, and then if a second touch happens while the first is still down, it
|
||||
will have index 1, etc.
|
||||
*/
|
||||
int getIndex() const;
|
||||
|
||||
/** Returns true if this device is currently being pressed. */
|
||||
bool isDragging() const;
|
||||
|
||||
/** Returns the last-known screen position of this source. */
|
||||
Point<float> getScreenPosition() const;
|
||||
|
||||
/** Returns a set of modifiers that indicate which buttons are currently
|
||||
held down on this device.
|
||||
*/
|
||||
ModifierKeys getCurrentModifiers() const;
|
||||
|
||||
/** Returns the component that was last known to be under this pointer. */
|
||||
Component* getComponentUnderMouse() const;
|
||||
|
||||
/** Tells the device to dispatch a mouse-move or mouse-drag event.
|
||||
This is asynchronous - the event will occur on the message thread.
|
||||
*/
|
||||
void triggerFakeMove() const;
|
||||
|
||||
/** Returns the number of clicks that should be counted as belonging to the
|
||||
current mouse event.
|
||||
So the mouse is currently down and it's the second click of a double-click, this
|
||||
will return 2.
|
||||
*/
|
||||
int getNumberOfMultipleClicks() const noexcept;
|
||||
|
||||
/** Returns the time at which the last mouse-down occurred. */
|
||||
Time getLastMouseDownTime() const noexcept;
|
||||
|
||||
/** Returns the screen position at which the last mouse-down occurred. */
|
||||
Point<float> getLastMouseDownPosition() const noexcept;
|
||||
|
||||
/** Returns true if this mouse is currently down, and if it has been dragged more
|
||||
than a couple of pixels from the place it was pressed.
|
||||
*/
|
||||
bool hasMouseMovedSignificantlySincePressed() const noexcept;
|
||||
|
||||
/** Returns true if this input source uses a visible mouse cursor. */
|
||||
bool hasMouseCursor() const noexcept;
|
||||
|
||||
/** Changes the mouse cursor, (if there is one). */
|
||||
void showMouseCursor (const MouseCursor& cursor);
|
||||
|
||||
/** Hides the mouse cursor (if there is one). */
|
||||
void hideCursor();
|
||||
|
||||
/** Un-hides the mouse cursor if it was hidden by hideCursor(). */
|
||||
void revealCursor();
|
||||
|
||||
/** Forces an update of the mouse cursor for whatever component it's currently over. */
|
||||
void forceMouseCursorUpdate();
|
||||
|
||||
/** Returns true if this mouse can be moved indefinitely in any direction without running out of space. */
|
||||
bool canDoUnboundedMovement() const noexcept;
|
||||
|
||||
/** Allows the mouse to move beyond the edges of the screen.
|
||||
|
||||
Calling this method when the mouse button is currently pressed will remove the cursor
|
||||
from the screen and allow the mouse to (seem to) move beyond the edges of the screen.
|
||||
|
||||
This means that the coordinates returned to mouseDrag() will be unbounded, and this
|
||||
can be used for things like custom slider controls or dragging objects around, where
|
||||
movement would be otherwise be limited by the mouse hitting the edges of the screen.
|
||||
|
||||
The unbounded mode is automatically turned off when the mouse button is released, or
|
||||
it can be turned off explicitly by calling this method again.
|
||||
|
||||
@param isEnabled whether to turn this mode on or off
|
||||
@param keepCursorVisibleUntilOffscreen if set to false, the cursor will immediately be
|
||||
hidden; if true, it will only be hidden when it
|
||||
is moved beyond the edge of the screen
|
||||
*/
|
||||
void enableUnboundedMouseMovement (bool isEnabled, bool keepCursorVisibleUntilOffscreen = false) const;
|
||||
|
||||
/** Returns true if this source is currently in "unbounded" mode. */
|
||||
bool isUnboundedMouseMovementEnabled() const;
|
||||
|
||||
/** Attempts to set this mouse pointer's screen position. */
|
||||
void setScreenPosition (Point<float> newPosition);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class ComponentPeer;
|
||||
friend class Desktop;
|
||||
friend class MouseInputSourceInternal;
|
||||
MouseInputSourceInternal* pimpl;
|
||||
|
||||
struct SourceList;
|
||||
|
||||
explicit MouseInputSource (MouseInputSourceInternal*) noexcept;
|
||||
void handleEvent (ComponentPeer&, Point<float>, int64 time, ModifierKeys);
|
||||
void handleWheel (ComponentPeer&, Point<float>, int64 time, const MouseWheelDetails&);
|
||||
void handleMagnifyGesture (ComponentPeer&, Point<float>, int64 time, float scaleFactor);
|
||||
|
||||
static Point<float> getCurrentRawMousePosition();
|
||||
static void setRawMousePosition (Point<float>);
|
||||
|
||||
JUCE_LEAK_DETECTOR (MouseInputSource)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_MOUSEINPUTSOURCE_H_INCLUDED
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
void MouseListener::mouseEnter (const MouseEvent&) {}
|
||||
void MouseListener::mouseExit (const MouseEvent&) {}
|
||||
void MouseListener::mouseDown (const MouseEvent&) {}
|
||||
void MouseListener::mouseUp (const MouseEvent&) {}
|
||||
void MouseListener::mouseDrag (const MouseEvent&) {}
|
||||
void MouseListener::mouseMove (const MouseEvent&) {}
|
||||
void MouseListener::mouseDoubleClick (const MouseEvent&) {}
|
||||
void MouseListener::mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) {}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_MOUSELISTENER_H_INCLUDED
|
||||
#define JUCE_MOUSELISTENER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A MouseListener can be registered with a component to receive callbacks
|
||||
about mouse events that happen to that component.
|
||||
|
||||
@see Component::addMouseListener, Component::removeMouseListener
|
||||
*/
|
||||
class JUCE_API MouseListener
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~MouseListener() {}
|
||||
|
||||
/** Called when the mouse moves inside a component.
|
||||
|
||||
If the mouse button isn't pressed and the mouse moves over a component,
|
||||
this will be called to let the component react to this.
|
||||
|
||||
A component will always get a mouseEnter callback before a mouseMove.
|
||||
|
||||
@param event details about the position and status of the mouse event, including
|
||||
the source component in which it occurred
|
||||
@see mouseEnter, mouseExit, mouseDrag, contains
|
||||
*/
|
||||
virtual void mouseMove (const MouseEvent& event);
|
||||
|
||||
/** Called when the mouse first enters a component.
|
||||
|
||||
If the mouse button isn't pressed and the mouse moves into a component,
|
||||
this will be called to let the component react to this.
|
||||
|
||||
When the mouse button is pressed and held down while being moved in
|
||||
or out of a component, no mouseEnter or mouseExit callbacks are made - only
|
||||
mouseDrag messages are sent to the component that the mouse was originally
|
||||
clicked on, until the button is released.
|
||||
|
||||
@param event details about the position and status of the mouse event, including
|
||||
the source component in which it occurred
|
||||
@see mouseExit, mouseDrag, mouseMove, contains
|
||||
*/
|
||||
virtual void mouseEnter (const MouseEvent& event);
|
||||
|
||||
/** Called when the mouse moves out of a component.
|
||||
|
||||
This will be called when the mouse moves off the edge of this
|
||||
component.
|
||||
|
||||
If the mouse button was pressed, and it was then dragged off the
|
||||
edge of the component and released, then this callback will happen
|
||||
when the button is released, after the mouseUp callback.
|
||||
|
||||
@param event details about the position and status of the mouse event, including
|
||||
the source component in which it occurred
|
||||
@see mouseEnter, mouseDrag, mouseMove, contains
|
||||
*/
|
||||
virtual void mouseExit (const MouseEvent& event);
|
||||
|
||||
/** Called when a mouse button is pressed.
|
||||
|
||||
The MouseEvent object passed in contains lots of methods for finding out
|
||||
which button was pressed, as well as which modifier keys (e.g. shift, ctrl)
|
||||
were held down at the time.
|
||||
|
||||
Once a button is held down, the mouseDrag method will be called when the
|
||||
mouse moves, until the button is released.
|
||||
|
||||
@param event details about the position and status of the mouse event, including
|
||||
the source component in which it occurred
|
||||
@see mouseUp, mouseDrag, mouseDoubleClick, contains
|
||||
*/
|
||||
virtual void mouseDown (const MouseEvent& event);
|
||||
|
||||
/** Called when the mouse is moved while a button is held down.
|
||||
|
||||
When a mouse button is pressed inside a component, that component
|
||||
receives mouseDrag callbacks each time the mouse moves, even if the
|
||||
mouse strays outside the component's bounds.
|
||||
|
||||
@param event details about the position and status of the mouse event, including
|
||||
the source component in which it occurred
|
||||
@see mouseDown, mouseUp, mouseMove, contains, setDragRepeatInterval
|
||||
*/
|
||||
virtual void mouseDrag (const MouseEvent& event);
|
||||
|
||||
/** Called when a mouse button is released.
|
||||
|
||||
A mouseUp callback is sent to the component in which a button was pressed
|
||||
even if the mouse is actually over a different component when the
|
||||
button is released.
|
||||
|
||||
The MouseEvent object passed in contains lots of methods for finding out
|
||||
which buttons were down just before they were released.
|
||||
|
||||
@param event details about the position and status of the mouse event, including
|
||||
the source component in which it occurred
|
||||
@see mouseDown, mouseDrag, mouseDoubleClick, contains
|
||||
*/
|
||||
virtual void mouseUp (const MouseEvent& event);
|
||||
|
||||
/** Called when a mouse button has been double-clicked on a component.
|
||||
|
||||
The MouseEvent object passed in contains lots of methods for finding out
|
||||
which button was pressed, as well as which modifier keys (e.g. shift, ctrl)
|
||||
were held down at the time.
|
||||
|
||||
@param event details about the position and status of the mouse event, including
|
||||
the source component in which it occurred
|
||||
@see mouseDown, mouseUp
|
||||
*/
|
||||
virtual void mouseDoubleClick (const MouseEvent& event);
|
||||
|
||||
/** Called when the mouse-wheel is moved.
|
||||
|
||||
This callback is sent to the component that the mouse is over when the
|
||||
wheel is moved.
|
||||
|
||||
If not overridden, a component will forward this message to its parent, so
|
||||
that parent components can collect mouse-wheel messages that happen to
|
||||
child components which aren't interested in them.
|
||||
|
||||
@param event details about the mouse event
|
||||
@param wheel details about the wheel movement
|
||||
*/
|
||||
virtual void mouseWheelMove (const MouseEvent& event,
|
||||
const MouseWheelDetails& wheel);
|
||||
|
||||
|
||||
private:
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
// This is just here to cause a compile error in old code that hasn't been
|
||||
// updated to use the new version of this method.
|
||||
virtual int mouseWheelMove (const MouseEvent&, float, float) { return 0; }
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_MOUSELISTENER_H_INCLUDED
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SELECTEDITEMSET_H_INCLUDED
|
||||
#define JUCE_SELECTEDITEMSET_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Manages a list of selectable items.
|
||||
|
||||
Use one of these to keep a track of things that the user has highlighted, like
|
||||
icons or things in a list.
|
||||
|
||||
The class is templated so that you can use it to hold either a set of pointers
|
||||
to objects, or a set of ID numbers or handles, for cases where each item may
|
||||
not always have a corresponding object.
|
||||
|
||||
To be informed when items are selected/deselected, register a ChangeListener with
|
||||
this object.
|
||||
|
||||
@see SelectableObject
|
||||
*/
|
||||
template <class SelectableItemType>
|
||||
class SelectedItemSet : public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
typedef SelectableItemType ItemType;
|
||||
typedef Array<SelectableItemType> ItemArray;
|
||||
typedef PARAMETER_TYPE (SelectableItemType) ParameterType;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates an empty set. */
|
||||
SelectedItemSet()
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a set based on an array of items. */
|
||||
explicit SelectedItemSet (const ItemArray& items)
|
||||
: selectedItems (items)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
SelectedItemSet (const SelectedItemSet& other)
|
||||
: selectedItems (other.selectedItems)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a copy of another set. */
|
||||
SelectedItemSet& operator= (const SelectedItemSet& other)
|
||||
{
|
||||
if (selectedItems != other.selectedItems)
|
||||
{
|
||||
selectedItems = other.selectedItems;
|
||||
changed();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Clears any other currently selected items, and selects this item.
|
||||
|
||||
If this item is already the only thing selected, no change notification
|
||||
will be sent out.
|
||||
|
||||
@see addToSelection, addToSelectionBasedOnModifiers
|
||||
*/
|
||||
void selectOnly (ParameterType item)
|
||||
{
|
||||
if (isSelected (item))
|
||||
{
|
||||
for (int i = selectedItems.size(); --i >= 0;)
|
||||
{
|
||||
if (selectedItems.getUnchecked(i) != item)
|
||||
{
|
||||
deselect (selectedItems.getUnchecked(i));
|
||||
i = jmin (i, selectedItems.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
deselectAll();
|
||||
changed();
|
||||
|
||||
selectedItems.add (item);
|
||||
itemSelected (item);
|
||||
}
|
||||
}
|
||||
|
||||
/** Selects an item.
|
||||
If the item is already selected, no change notification will be sent out.
|
||||
@see selectOnly, addToSelectionBasedOnModifiers
|
||||
*/
|
||||
void addToSelection (ParameterType item)
|
||||
{
|
||||
if (! isSelected (item))
|
||||
{
|
||||
changed();
|
||||
|
||||
selectedItems.add (item);
|
||||
itemSelected (item);
|
||||
}
|
||||
}
|
||||
|
||||
/** Selects or deselects an item.
|
||||
|
||||
This will use the modifier keys to decide whether to deselect other items
|
||||
first.
|
||||
|
||||
So if the shift key is held down, the item will be added without deselecting
|
||||
anything (same as calling addToSelection() )
|
||||
|
||||
If no modifiers are down, the current selection will be cleared first (same
|
||||
as calling selectOnly() )
|
||||
|
||||
If the ctrl (or command on the Mac) key is held down, the item will be toggled -
|
||||
so it'll be added to the set unless it's already there, in which case it'll be
|
||||
deselected.
|
||||
|
||||
If the items that you're selecting can also be dragged, you may need to use the
|
||||
addToSelectionOnMouseDown() and addToSelectionOnMouseUp() calls to handle the
|
||||
subtleties of this kind of usage.
|
||||
|
||||
@see selectOnly, addToSelection, addToSelectionOnMouseDown, addToSelectionOnMouseUp
|
||||
*/
|
||||
void addToSelectionBasedOnModifiers (ParameterType item,
|
||||
ModifierKeys modifiers)
|
||||
{
|
||||
if (modifiers.isShiftDown())
|
||||
{
|
||||
addToSelection (item);
|
||||
}
|
||||
else if (modifiers.isCommandDown())
|
||||
{
|
||||
if (isSelected (item))
|
||||
deselect (item);
|
||||
else
|
||||
addToSelection (item);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectOnly (item);
|
||||
}
|
||||
}
|
||||
|
||||
/** Selects or deselects items that can also be dragged, based on a mouse-down event.
|
||||
|
||||
If you call addToSelectionOnMouseDown() at the start of your mouseDown event,
|
||||
and then call addToSelectionOnMouseUp() at the end of your mouseUp event, this
|
||||
makes it easy to handle multiple-selection of sets of objects that can also
|
||||
be dragged.
|
||||
|
||||
For example, if you have several items already selected, and you click on
|
||||
one of them (without dragging), then you'd expect this to deselect the other, and
|
||||
just select the item you clicked on. But if you had clicked on this item and
|
||||
dragged it, you'd have expected them all to stay selected.
|
||||
|
||||
When you call this method, you'll need to store the boolean result, because the
|
||||
addToSelectionOnMouseUp() method will need to be know this value.
|
||||
|
||||
@see addToSelectionOnMouseUp, addToSelectionBasedOnModifiers
|
||||
*/
|
||||
bool addToSelectionOnMouseDown (ParameterType item,
|
||||
ModifierKeys modifiers)
|
||||
{
|
||||
if (isSelected (item))
|
||||
return ! modifiers.isPopupMenu();
|
||||
|
||||
addToSelectionBasedOnModifiers (item, modifiers);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Selects or deselects items that can also be dragged, based on a mouse-up event.
|
||||
|
||||
Call this during a mouseUp callback, when you have previously called the
|
||||
addToSelectionOnMouseDown() method during your mouseDown event.
|
||||
|
||||
See addToSelectionOnMouseDown() for more info
|
||||
|
||||
@param item the item to select (or deselect)
|
||||
@param modifiers the modifiers from the mouse-up event
|
||||
@param wasItemDragged true if your item was dragged during the mouse click
|
||||
@param resultOfMouseDownSelectMethod this is the boolean return value that came
|
||||
back from the addToSelectionOnMouseDown() call that you
|
||||
should have made during the matching mouseDown event
|
||||
*/
|
||||
void addToSelectionOnMouseUp (ParameterType item,
|
||||
ModifierKeys modifiers,
|
||||
const bool wasItemDragged,
|
||||
const bool resultOfMouseDownSelectMethod)
|
||||
{
|
||||
if (resultOfMouseDownSelectMethod && ! wasItemDragged)
|
||||
addToSelectionBasedOnModifiers (item, modifiers);
|
||||
}
|
||||
|
||||
/** Deselects an item. */
|
||||
void deselect (ParameterType item)
|
||||
{
|
||||
const int i = selectedItems.indexOf (item);
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
changed();
|
||||
itemDeselected (selectedItems.remove (i));
|
||||
}
|
||||
}
|
||||
|
||||
/** Deselects all items. */
|
||||
void deselectAll()
|
||||
{
|
||||
if (selectedItems.size() > 0)
|
||||
{
|
||||
changed();
|
||||
|
||||
for (int i = selectedItems.size(); --i >= 0;)
|
||||
{
|
||||
itemDeselected (selectedItems.remove (i));
|
||||
i = jmin (i, selectedItems.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of currently selected items.
|
||||
@see getSelectedItem
|
||||
*/
|
||||
int getNumSelected() const noexcept { return selectedItems.size(); }
|
||||
|
||||
/** Returns one of the currently selected items.
|
||||
If the index is out-of-range, this returns a default-constructed SelectableItemType.
|
||||
@see getNumSelected
|
||||
*/
|
||||
SelectableItemType getSelectedItem (const int index) const { return selectedItems [index]; }
|
||||
|
||||
/** True if this item is currently selected. */
|
||||
bool isSelected (ParameterType item) const noexcept { return selectedItems.contains (item); }
|
||||
|
||||
/** Provides access to the array of items. */
|
||||
const ItemArray& getItemArray() const noexcept { return selectedItems; }
|
||||
|
||||
/** Provides iterator access to the array of items. */
|
||||
SelectableItemType* begin() const noexcept { return selectedItems.begin(); }
|
||||
|
||||
/** Provides iterator access to the array of items. */
|
||||
SelectableItemType* end() const noexcept { return selectedItems.end(); }
|
||||
|
||||
//==============================================================================
|
||||
/** Can be overridden to do special handling when an item is selected.
|
||||
|
||||
For example, if the item is an object, you might want to call it and tell
|
||||
it that it's being selected.
|
||||
*/
|
||||
virtual void itemSelected (SelectableItemType) {}
|
||||
|
||||
/** Can be overridden to do special handling when an item is deselected.
|
||||
|
||||
For example, if the item is an object, you might want to call it and tell
|
||||
it that it's being deselected.
|
||||
*/
|
||||
virtual void itemDeselected (SelectableItemType) {}
|
||||
|
||||
/** Used internally, but can be called to force a change message to be sent
|
||||
to the ChangeListeners.
|
||||
*/
|
||||
void changed()
|
||||
{
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
/** Used internally, but can be called to force a change message to be sent
|
||||
to the ChangeListeners.
|
||||
*/
|
||||
void changed (const bool synchronous)
|
||||
{
|
||||
if (synchronous)
|
||||
sendSynchronousChangeMessage();
|
||||
else
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ItemArray selectedItems;
|
||||
|
||||
JUCE_LEAK_DETECTOR (SelectedItemSet<SelectableItemType>)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_SELECTEDITEMSET_H_INCLUDED
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_TEXTDRAGANDDROPTARGET_H_INCLUDED
|
||||
#define JUCE_TEXTDRAGANDDROPTARGET_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Components derived from this class can have text dropped onto them by an external application.
|
||||
|
||||
@see DragAndDropContainer
|
||||
*/
|
||||
class JUCE_API TextDragAndDropTarget
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~TextDragAndDropTarget() {}
|
||||
|
||||
/** Callback to check whether this target is interested in the set of text being offered.
|
||||
|
||||
Note that this will be called repeatedly when the user is dragging the mouse around over your
|
||||
component, so don't do anything time-consuming in here!
|
||||
|
||||
@param text the text that the user is dragging
|
||||
@returns true if this component wants to receive the other callbacks regarging this
|
||||
type of object; if it returns false, no other callbacks will be made.
|
||||
*/
|
||||
virtual bool isInterestedInTextDrag (const String& text) = 0;
|
||||
|
||||
/** Callback to indicate that some text is being dragged over this component.
|
||||
|
||||
This gets called when the user moves the mouse into this component while dragging.
|
||||
|
||||
Use this callback as a trigger to make your component repaint itself to give the
|
||||
user feedback about whether the text can be dropped here or not.
|
||||
|
||||
@param text the text that the user is dragging
|
||||
@param x the mouse x position, relative to this component
|
||||
@param y the mouse y position, relative to this component
|
||||
*/
|
||||
virtual void textDragEnter (const String& text, int x, int y);
|
||||
|
||||
/** Callback to indicate that the user is dragging some text over this component.
|
||||
|
||||
This gets called when the user moves the mouse over this component while dragging.
|
||||
Normally overriding itemDragEnter() and itemDragExit() are enough, but
|
||||
this lets you know what happens in-between.
|
||||
|
||||
@param text the text that the user is dragging
|
||||
@param x the mouse x position, relative to this component
|
||||
@param y the mouse y position, relative to this component
|
||||
*/
|
||||
virtual void textDragMove (const String& text, int x, int y);
|
||||
|
||||
/** Callback to indicate that the mouse has moved away from this component.
|
||||
|
||||
This gets called when the user moves the mouse out of this component while dragging
|
||||
the text.
|
||||
|
||||
If you've used textDragEnter() to repaint your component and give feedback, use this
|
||||
as a signal to repaint it in its normal state.
|
||||
|
||||
@param text the text that the user is dragging
|
||||
*/
|
||||
virtual void textDragExit (const String& text);
|
||||
|
||||
/** Callback to indicate that the user has dropped the text onto this component.
|
||||
|
||||
When the user drops the text, this get called, and you can use the text in whatever
|
||||
way is appropriate.
|
||||
|
||||
Note that after this is called, the textDragExit method may not be called, so you should
|
||||
clean up in here if there's anything you need to do when the drag finishes.
|
||||
|
||||
@param text the text that the user is dragging
|
||||
@param x the mouse x position, relative to this component
|
||||
@param y the mouse y position, relative to this component
|
||||
*/
|
||||
virtual void textDropped (const String& text, int x, int y) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_TEXTDRAGANDDROPTARGET_H_INCLUDED
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
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.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_TOOLTIPCLIENT_H_INCLUDED
|
||||
#define JUCE_TOOLTIPCLIENT_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Components that want to use pop-up tooltips should implement this interface.
|
||||
|
||||
A TooltipWindow will wait for the mouse to hover over a component that
|
||||
implements the TooltipClient interface, and when it finds one, it will display
|
||||
the tooltip returned by its getTooltip() method.
|
||||
|
||||
@see TooltipWindow, SettableTooltipClient
|
||||
*/
|
||||
class JUCE_API TooltipClient
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~TooltipClient() {}
|
||||
|
||||
/** Returns the string that this object wants to show as its tooltip. */
|
||||
virtual String getTooltip() = 0;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An implementation of TooltipClient that stores the tooltip string and a method
|
||||
for changing it.
|
||||
|
||||
This makes it easy to add a tooltip to a custom component, by simply adding this
|
||||
as a base class and calling setTooltip().
|
||||
|
||||
Many of the Juce widgets already use this as a base class to implement their
|
||||
tooltips.
|
||||
|
||||
@see TooltipClient, TooltipWindow
|
||||
*/
|
||||
class JUCE_API SettableTooltipClient : public TooltipClient
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~SettableTooltipClient() {}
|
||||
|
||||
//==============================================================================
|
||||
/** Assigns a new tooltip to this object. */
|
||||
virtual void setTooltip (const String& newTooltip) { tooltipString = newTooltip; }
|
||||
|
||||
/** Returns the tooltip assigned to this object. */
|
||||
virtual String getTooltip() { return tooltipString; }
|
||||
|
||||
protected:
|
||||
SettableTooltipClient() {}
|
||||
|
||||
private:
|
||||
String tooltipString;
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_TOOLTIPCLIENT_H_INCLUDED
|
||||
Loading…
Add table
Add a link
Reference in a new issue