* A mouse shake detecting BMessageFilter that's easily integrated in any app.
* A test app for it. I added a src/test/kits/shared folder as i found it was the most logical place for it. Shake it up. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32602 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Alexandre Deckner, [email protected]
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef SHAKE_TRACKING_FILTER_H
|
||||||
|
#define SHAKE_TRACKING_FILTER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <MessageFilter.h>
|
||||||
|
#include <Point.h>
|
||||||
|
|
||||||
|
class BView;
|
||||||
|
class BHandler;
|
||||||
|
class BMessageRunner;
|
||||||
|
|
||||||
|
namespace BPrivate {
|
||||||
|
|
||||||
|
class LowPassFilter {
|
||||||
|
public:
|
||||||
|
LowPassFilter(uint32 size);
|
||||||
|
~LowPassFilter();
|
||||||
|
|
||||||
|
void Input(const BPoint& p);
|
||||||
|
BPoint Output() const;
|
||||||
|
private:
|
||||||
|
BPoint* fPoints;
|
||||||
|
uint32 fSize;
|
||||||
|
BPoint fSum;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ShakeTrackingFilter : public BMessageFilter {
|
||||||
|
public:
|
||||||
|
ShakeTrackingFilter(
|
||||||
|
BView* targetView,
|
||||||
|
uint32 messageWhat,
|
||||||
|
uint32 countThreshold = 2,
|
||||||
|
bigtime_t timeTreshold = 400000);
|
||||||
|
|
||||||
|
~ShakeTrackingFilter();
|
||||||
|
|
||||||
|
filter_result Filter(BMessage* message, BHandler** _target);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BView* fTargetView;
|
||||||
|
uint32 fMessageWhat;
|
||||||
|
|
||||||
|
BMessageRunner* fCancelRunner;
|
||||||
|
LowPassFilter fLowPass;
|
||||||
|
BPoint fLastDelta;
|
||||||
|
BPoint fLastPosition;
|
||||||
|
uint32 fCounter;
|
||||||
|
uint32 fCountThreshold;
|
||||||
|
bigtime_t fTimeThreshold;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace BPrivate
|
||||||
|
|
||||||
|
using BPrivate::ShakeTrackingFilter;
|
||||||
|
using BPrivate::LowPassFilter;
|
||||||
|
|
||||||
|
#endif // SHAKE_TRACKING_FILTER_H
|
||||||
@@ -20,6 +20,7 @@ StaticLibrary libshared.a :
|
|||||||
DragTrackingFilter.cpp
|
DragTrackingFilter.cpp
|
||||||
HashString.cpp
|
HashString.cpp
|
||||||
RWLockManager.cpp
|
RWLockManager.cpp
|
||||||
|
ShakeTrackingFilter.cpp
|
||||||
Variant.cpp
|
Variant.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Alexandre Deckner, [email protected]
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class ShakeTrackingFilter
|
||||||
|
\brief A simple mouse shake detection filter
|
||||||
|
*
|
||||||
|
* A simple mouse filter that detects quick mouse shakes.
|
||||||
|
*
|
||||||
|
* It's detecting rough edges (u-turns) in the mouse movement
|
||||||
|
* and counts them within a time window.
|
||||||
|
* You can configure the message sent, the u-turn count threshold
|
||||||
|
* and the time threshold.
|
||||||
|
* It sends the count along with the message.
|
||||||
|
* For now, detection is limited within the view bounds, but
|
||||||
|
* it might be modified to accept a BRegion mask.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ShakeTrackingFilter.h>
|
||||||
|
|
||||||
|
#include <Message.h>
|
||||||
|
#include <Messenger.h>
|
||||||
|
#include <MessageRunner.h>
|
||||||
|
#include <View.h>
|
||||||
|
|
||||||
|
|
||||||
|
ShakeTrackingFilter::ShakeTrackingFilter(BView* targetView, uint32 messageWhat,
|
||||||
|
uint32 countThreshold, bigtime_t timeThreshold)
|
||||||
|
:
|
||||||
|
BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
|
||||||
|
fTargetView(targetView),
|
||||||
|
fMessageWhat(messageWhat),
|
||||||
|
fCancelRunner(NULL),
|
||||||
|
fLowPass(8),
|
||||||
|
fLastDelta(0, 0),
|
||||||
|
fCounter(0),
|
||||||
|
fCountThreshold(countThreshold),
|
||||||
|
fTimeThreshold(timeThreshold)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ShakeTrackingFilter::~ShakeTrackingFilter()
|
||||||
|
{
|
||||||
|
delete fCancelRunner;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
filter_result
|
||||||
|
ShakeTrackingFilter::Filter(BMessage* message, BHandler** /*_target*/)
|
||||||
|
{
|
||||||
|
if (fTargetView == NULL)
|
||||||
|
return B_DISPATCH_MESSAGE;
|
||||||
|
|
||||||
|
switch (message->what) {
|
||||||
|
case B_MOUSE_MOVED:
|
||||||
|
{
|
||||||
|
BPoint position;
|
||||||
|
message->FindPoint("be:view_where", &position);
|
||||||
|
|
||||||
|
// TODO: allow using BRegion masks
|
||||||
|
if (!fTargetView->Bounds().Contains(position))
|
||||||
|
return B_DISPATCH_MESSAGE;
|
||||||
|
|
||||||
|
fLowPass.Input(position - fLastPosition);
|
||||||
|
|
||||||
|
BPoint delta = fLowPass.Output();
|
||||||
|
|
||||||
|
// normalized dot product
|
||||||
|
float norm = delta.x * delta.x + delta.y * delta.y;
|
||||||
|
if (norm > 0.01) {
|
||||||
|
delta.x /= norm;
|
||||||
|
delta.y /= norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
norm = fLastDelta.x * fLastDelta.x + fLastDelta.y * fLastDelta.y;
|
||||||
|
if (norm > 0.01) {
|
||||||
|
fLastDelta.x /= norm;
|
||||||
|
fLastDelta.y /= norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dot = delta.x * fLastDelta.x + delta.y * fLastDelta.y;
|
||||||
|
|
||||||
|
if (dot < 0.0) {
|
||||||
|
if (fCounter == 0) {
|
||||||
|
BMessage * cancelMessage = new BMessage(kMsgCancel);
|
||||||
|
fCancelRunner = new BMessageRunner(BMessenger(fTargetView),
|
||||||
|
cancelMessage, fTimeThreshold, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fCounter++;
|
||||||
|
|
||||||
|
if (fCounter >= fCountThreshold) {
|
||||||
|
BMessage shakeMessage(fMessageWhat);
|
||||||
|
shakeMessage.AddUInt32("count", fCounter);
|
||||||
|
BMessenger messenger(fTargetView);
|
||||||
|
messenger.SendMessage(&shakeMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fLastDelta = fLowPass.Output();
|
||||||
|
fLastPosition = position;
|
||||||
|
|
||||||
|
return B_DISPATCH_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case kMsgCancel:
|
||||||
|
delete fCancelRunner;
|
||||||
|
fCancelRunner = NULL;
|
||||||
|
fCounter = 0;
|
||||||
|
return B_SKIP_MESSAGE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_DISPATCH_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
LowPassFilter::LowPassFilter(uint32 size)
|
||||||
|
:
|
||||||
|
fSize(size)
|
||||||
|
{
|
||||||
|
fPoints = new BPoint[fSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LowPassFilter::~LowPassFilter()
|
||||||
|
{
|
||||||
|
delete [] fPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LowPassFilter::Input(const BPoint& p)
|
||||||
|
{
|
||||||
|
// A fifo buffer that maintains a sum of its elements
|
||||||
|
fSum -= fPoints[0];
|
||||||
|
for (uint32 i = 0; i < fSize - 1; i++)
|
||||||
|
fPoints[i] = fPoints[i + 1];
|
||||||
|
fPoints[fSize - 1] = p;
|
||||||
|
fSum += p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BPoint
|
||||||
|
LowPassFilter::Output() const
|
||||||
|
{
|
||||||
|
return BPoint(fSum.x / (float) fSize, fSum.y / (float) fSize);
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src tests kits locale ;
|
|||||||
SubInclude HAIKU_TOP src tests kits media ;
|
SubInclude HAIKU_TOP src tests kits media ;
|
||||||
SubInclude HAIKU_TOP src tests kits midi ;
|
SubInclude HAIKU_TOP src tests kits midi ;
|
||||||
SubInclude HAIKU_TOP src tests kits net ;
|
SubInclude HAIKU_TOP src tests kits net ;
|
||||||
|
SubInclude HAIKU_TOP src tests kits shared ;
|
||||||
SubInclude HAIKU_TOP src tests kits storage ;
|
SubInclude HAIKU_TOP src tests kits storage ;
|
||||||
SubInclude HAIKU_TOP src tests kits support ;
|
SubInclude HAIKU_TOP src tests kits support ;
|
||||||
SubInclude HAIKU_TOP src tests kits translation ;
|
SubInclude HAIKU_TOP src tests kits translation ;
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
SubDir HAIKU_TOP src tests kits shared ;
|
||||||
|
|
||||||
|
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||||
|
AddSubDirSupportedPlatforms libbe_test ;
|
||||||
|
|
||||||
|
SubInclude HAIKU_TOP src tests kits shared shake_filter ;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
SubDir HAIKU_TOP src tests kits shared shake_filter ;
|
||||||
|
|
||||||
|
UsePrivateHeaders shared ;
|
||||||
|
|
||||||
|
Application ShakeFilterTest :
|
||||||
|
ShakeFilterTest.cpp
|
||||||
|
: libshared.a be $(TARGET_LIBSUPC++)
|
||||||
|
;
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Alexandre Deckner <[email protected]>
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <Application.h>
|
||||||
|
#include <ShakeTrackingFilter.h>
|
||||||
|
#include <Window.h>
|
||||||
|
#include <View.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
static const uint32 kMsgShake = 'Shke';
|
||||||
|
|
||||||
|
|
||||||
|
class View : public BView {
|
||||||
|
public:
|
||||||
|
View(BRect rect, const char* name, uint32 followMode);
|
||||||
|
virtual ~View();
|
||||||
|
|
||||||
|
virtual void AttachedToWindow();
|
||||||
|
virtual void MessageReceived(BMessage* msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
View::View(BRect rect, const char* name, uint32 followMode)
|
||||||
|
:
|
||||||
|
BView(rect, name, followMode, B_WILL_DRAW)
|
||||||
|
{
|
||||||
|
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
||||||
|
SetHighColor(255, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
View::~View()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
View::AttachedToWindow()
|
||||||
|
{
|
||||||
|
AddFilter(new ShakeTrackingFilter(this, kMsgShake, 3, 500000));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
View::MessageReceived(BMessage* msg)
|
||||||
|
{
|
||||||
|
if (msg->what == kMsgShake) {
|
||||||
|
uint32 count = 0;
|
||||||
|
msg->FindUInt32("count", &count);
|
||||||
|
printf("Shake! %lu\n", count);
|
||||||
|
} else {
|
||||||
|
BView::MessageReceived(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
BApplication app("application/x-vnd.haiku-ShakeFilterTest");
|
||||||
|
|
||||||
|
BWindow* window = new BWindow(BRect(100, 100, 400, 400),
|
||||||
|
"ShakeFilter-Test", B_TITLED_WINDOW,
|
||||||
|
B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE);
|
||||||
|
|
||||||
|
BRect frame = window->Bounds();
|
||||||
|
|
||||||
|
window->AddChild(new View(frame, "L ", B_FOLLOW_ALL));
|
||||||
|
window->Show();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user