Browse Source

New classes: AnimatedPosition and MouseInactivityDetector.

tags/2021-05-28
jules 12 years ago
parent
commit
d3a207c596
7 changed files with 544 additions and 2 deletions
  1. +2
    -2
      modules/juce_graphics/juce_graphics.h
  2. +1
    -0
      modules/juce_gui_basics/juce_gui_basics.cpp
  3. +9
    -0
      modules/juce_gui_basics/juce_gui_basics.h
  4. +208
    -0
      modules/juce_gui_basics/layout/juce_AnimatedPosition.h
  5. +149
    -0
      modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h
  6. +70
    -0
      modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.cpp
  7. +105
    -0
      modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.h

+ 2
- 2
modules/juce_graphics/juce_graphics.h View File

@@ -22,7 +22,7 @@
==============================================================================
*/
#ifndef __JUCE_GRAPHICS_MODULE_JUCEHEADER__
#ifndef __JUCE_GRAPHICS_MODULE_JUCEHEADER__ // %%
#define __JUCE_GRAPHICS_MODULE_JUCEHEADER__
#include "../juce_core/juce_core.h"
@@ -172,4 +172,4 @@ namespace juce
}
#endif // __JUCE_GRAPHICS_JUCEHEADER__
#endif // __JUCE_GRAPHICS_MODULE_JUCEHEADER__

+ 1
- 0
modules/juce_gui_basics/juce_gui_basics.cpp View File

@@ -147,6 +147,7 @@ namespace juce
#include "mouse/juce_DragAndDropContainer.cpp"
#include "mouse/juce_MouseCursor.cpp"
#include "mouse/juce_MouseEvent.cpp"
#include "mouse/juce_MouseInactivityDetector.cpp"
#include "mouse/juce_MouseInputSource.cpp"
#include "mouse/juce_MouseListener.cpp"
#include "keyboard/juce_CaretComponent.cpp"


+ 9
- 0
modules/juce_gui_basics/juce_gui_basics.h View File

@@ -111,6 +111,9 @@ namespace juce
#ifndef __JUCE_MOUSEEVENT_JUCEHEADER__
#include "mouse/juce_MouseEvent.h"
#endif
#ifndef __JUCE_MOUSEINACTIVITYDETECTOR_JUCEHEADER__
#include "mouse/juce_MouseInactivityDetector.h"
#endif
#ifndef __JUCE_MOUSEINPUTSOURCE_JUCEHEADER__
#include "mouse/juce_MouseInputSource.h"
#endif
@@ -240,6 +243,12 @@ namespace juce
#ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__
#include "filebrowser/juce_WildcardFileFilter.h"
#endif
#ifndef __JUCE_ANIMATEDPOSITION_JUCEHEADER__
#include "layout/juce_AnimatedPosition.h"
#endif
#ifndef __JUCE_ANIMATEDPOSITIONBEHAVIOURS_JUCEHEADER__
#include "layout/juce_AnimatedPositionBehaviours.h"
#endif
#ifndef __JUCE_COMPONENTANIMATOR_JUCEHEADER__
#include "layout/juce_ComponentAnimator.h"
#endif


+ 208
- 0
modules/juce_gui_basics/layout/juce_AnimatedPosition.h View File

@@ -0,0 +1,208 @@
/*
==============================================================================
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_ANIMATEDPOSITION_JUCEHEADER__
#define __JUCE_ANIMATEDPOSITION_JUCEHEADER__
//==============================================================================
/**
Models a 1-dimensional position that can be dragged around by the user, and which
will then continue moving with a customisable physics behaviour when released.
This is useful for things like scrollable views or objects that can be dragged and
thrown around with the mouse/touch, and by writing your own behaviour class, you can
customise the trajectory that it follows when released.
The class uses its own Timer to continuously change its value when a drag ends, and
Listener objects can be registered to receive callbacks whenever the value changes.
The value is stored as a double, and can be used to represent whatever units you need.
The template parameter Behaviour must be a class that implements various methods to
return the physics of the value's movement - you can use the classes provided for this
in the AnimatedPositionBehaviours namespace, or write your own custom behaviour.
@see AnimatedPositionBehaviours::ContinuousWithMomentum,
AnimatedPositionBehaviours::SnapToPageBoundaries
*/
template <typename Behaviour>
class AnimatedPosition : private Timer
{
public:
AnimatedPosition()
: position(), grabbedPos(), releaseVelocity(),
range (-std::numeric_limits<double>::max(),
std::numeric_limits<double>::max())
{
}
/** Sets a range within which the value will be constrained. */
void setLimits (Range<double> newRange)
{
range = newRange;
}
//==============================================================================
/** Called to indicate that the object is now being controlled by a
mouse-drag or similar operation.
After calling this method, you should make calls to the drag() method
each time the mouse drags the position around, and always be sure to
finish with a call to endDrag() when the mouse is released, which allows
the position to continue moving freely according to the specified behaviour.
*/
void beginDrag()
{
grabbedPos = position;
releaseVelocity = 0;
stopTimer();
}
/** Called during a mouse-drag operation, to indicate that the mouse has moved.
The delta is the difference between the position when beginDrag() was called
and the new position that's required.
*/
void drag (double deltaFromStartOfDrag)
{
moveTo (grabbedPos + deltaFromStartOfDrag);
}
/** Called after beginDrag() and drag() to indicate that the drag operation has
now finished.
*/
void endDrag()
{
startTimer (1000 / 60);
}
/** Called outside of a drag operation to cause a nudge in the specified direction.
This is intended for use by e.g. mouse-wheel events.
*/
void nudge (double deltaFromCurrentPosition)
{
startTimer (100);
moveTo (position + deltaFromCurrentPosition);
}
//==============================================================================
/** Returns the current position. */
double getPosition() const noexcept
{
return position;
}
/** Explicitly sets the position and stops any further movement.
This will cause a synchronous call to any listeners if the position actually
changes.
*/
void setPosition (double newPosition)
{
stopTimer();
setPositionAndSendChange (newPosition);
}
//==============================================================================
/** Implement this class if you need to receive callbacks when the value of
an AnimatedPosition changes.
@see AnimatedPosition::addListener, AnimatedPosition::removeListener
*/
class Listener
{
public:
virtual ~Listener() {}
/** Called synchronously when an AnimatedPosition changes. */
virtual void positionChanged (AnimatedPosition&, double newPosition) = 0;
};
/** Adds a listener to be called when the value changes. */
void addListener (Listener* listener) { listeners.add (listener); }
/** Removes a previously-registered listener. */
void removeListener (Listener* listener) { listeners.remove (listener); }
//==============================================================================
/** The behaviour object.
This is public to let you tweak any parameters that it provides.
*/
Behaviour behaviour;
private:
//==============================================================================
double position, grabbedPos, releaseVelocity;
Range<double> range;
Time lastUpdate, lastDrag;
ListenerList<Listener> listeners;
static double getSpeed (const Time last, double lastPos,
const Time now, double newPos)
{
const double elapsedSecs = jmax (0.005, (now - last).inSeconds());
const double v = (newPos - lastPos) / elapsedSecs;
return std::abs (v) > 0.2 ? v : 0.0;
}
void moveTo (double newPos)
{
const Time now (Time::getCurrentTime());
releaseVelocity = getSpeed (lastDrag, position, now, newPos);
behaviour.releasedWithVelocity (newPos, releaseVelocity);
lastDrag = now;
setPositionAndSendChange (newPos);
}
void setPositionAndSendChange (double newPosition)
{
newPosition = range.clipValue (newPosition);
if (position != newPosition)
{
position = newPosition;
listeners.call (&Listener::positionChanged, *this, newPosition);
}
}
void timerCallback() override
{
const Time now = Time::getCurrentTime();
const double elapsed = jlimit (0.001, 0.020, (now - lastUpdate).inSeconds());
lastUpdate = now;
const double newPos = behaviour.getNextPosition (position, elapsed);
if (behaviour.isStopped (newPos))
stopTimer();
else
startTimer (1000 / 60);
setPositionAndSendChange (newPos);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedPosition)
};
#endif // __JUCE_ANIMATEDPOSITION_JUCEHEADER__

+ 149
- 0
modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h View File

@@ -0,0 +1,149 @@
/*
==============================================================================
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_ANIMATEDPOSITIONBEHAVIOURS_JUCEHEADER__
#define __JUCE_ANIMATEDPOSITIONBEHAVIOURS_JUCEHEADER__
//==============================================================================
/** Contains classes for different types of physics behaviours - these classes
are used as template parameters for the AnimatedPosition class.
*/
namespace AnimatedPositionBehaviours
{
/** A non-snapping behaviour that allows the content to be freely flicked in
either direction, with momentum based on the velocity at which it was
released, and variable friction to make it come to a halt.
This class is intended to be used as a template parameter to the
AnimatedPosition class.
@see AnimatedPosition
*/
struct ContinuousWithMomentum
{
ContinuousWithMomentum() noexcept
: velocity (0), damping (0.92)
{
}
/** Sets the friction that damps the movement of the value.
A typical value is 0.08; higher values indicate more friction.
*/
void setFriction (double newFriction) noexcept
{
damping = 1.0 - newFriction;
}
/** Called by the AnimatedPosition class. This tells us the position and
velocity at which the user is about to release the object.
The velocity is measured in units/second.
*/
void releasedWithVelocity (double /*position*/, double releaseVelocity) noexcept
{
velocity = releaseVelocity;
}
/** Called by the AnimatedPosition class to get the new position, after
the given time has elapsed.
*/
double getNextPosition (double oldPos, double elapsedSeconds) noexcept
{
velocity *= damping;
if (std::abs (velocity) < 0.05)
velocity = 0;
return oldPos + velocity * elapsedSeconds;
}
/** Called by the AnimatedPosition class to check whether the object
is now stationary.
*/
bool isStopped (double /*position*/) const noexcept
{
return velocity == 0;
}
private:
double velocity, damping;
};
//==============================================================================
/** A behaviour that gravitates an AnimatedPosition object towards the nearest
integer position when released.
This class is intended to be used as a template parameter to the
AnimatedPosition class. It's handy when using an AnimatedPosition to show a
series of pages, because it allows the pages can be scrolled smoothly, but when
released, snaps back to show a whole page.
@see AnimatedPosition
*/
struct SnapToPageBoundaries
{
SnapToPageBoundaries() noexcept : targetSnapPosition()
{
}
/** Called by the AnimatedPosition class. This tells us the position and
velocity at which the user is about to release the object.
The velocity is measured in units/second.
*/
void releasedWithVelocity (double position, double releaseVelocity) noexcept
{
targetSnapPosition = std::floor (position + 0.5);
if (releaseVelocity > 1.0 && targetSnapPosition < position) ++targetSnapPosition;
if (releaseVelocity < -1.0 && targetSnapPosition > position) --targetSnapPosition;
}
/** Called by the AnimatedPosition class to get the new position, after
the given time has elapsed.
*/
double getNextPosition (double oldPos, double elapsedSeconds) const noexcept
{
if (isStopped (oldPos))
return targetSnapPosition;
const double snapSpeed = 10.0;
const double velocity = (targetSnapPosition - oldPos) * snapSpeed;
return oldPos + velocity * elapsedSeconds;
}
/** Called by the AnimatedPosition class to check whether the object
is now stationary.
*/
bool isStopped (double position) const noexcept
{
return std::abs (targetSnapPosition - position) < 0.001;
}
private:
double targetSnapPosition;
};
};
#endif // __JUCE_ANIMATEDPOSITIONBEHAVIOURS_JUCEHEADER__

+ 70
- 0
modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.cpp View File

@@ -0,0 +1,70 @@
/*
==============================================================================
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);
lastMousePos = newPos;
startTimer (delayMs);
}
void MouseInactivityDetector::setActive (bool b)
{
if (isActive != b)
{
isActive = b;
listenerList.call (b ? &Listener::mouseBecameActive
: &Listener::mouseBecameInactive);
}
}

+ 105
- 0
modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.h View File

@@ -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_MOUSEINACTIVITYDETECTOR_JUCEHEADER__
#define __JUCE_MOUSEINACTIVITYDETECTOR_JUCEHEADER__
//==============================================================================
/**
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_JUCEHEADER__

Loading…
Cancel
Save