* 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:
parent
eb47b26534
commit
8e43b9e35e
62
headers/private/shared/ShakeTrackingFilter.h
Normal file
62
headers/private/shared/ShakeTrackingFilter.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2009, Alexandre Deckner, alex@zappotek.com
|
||||
* 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
|
||||
HashString.cpp
|
||||
RWLockManager.cpp
|
||||
ShakeTrackingFilter.cpp
|
||||
Variant.cpp
|
||||
;
|
||||
|
||||
|
157
src/kits/shared/ShakeTrackingFilter.cpp
Normal file
157
src/kits/shared/ShakeTrackingFilter.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2009, Alexandre Deckner, alex@zappotek.com
|
||||
* 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 midi ;
|
||||
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 support ;
|
||||
SubInclude HAIKU_TOP src tests kits translation ;
|
||||
|
6
src/tests/kits/shared/Jamfile
Normal file
6
src/tests/kits/shared/Jamfile
Normal file
@ -0,0 +1,6 @@
|
||||
SubDir HAIKU_TOP src tests kits shared ;
|
||||
|
||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
AddSubDirSupportedPlatforms libbe_test ;
|
||||
|
||||
SubInclude HAIKU_TOP src tests kits shared shake_filter ;
|
8
src/tests/kits/shared/shake_filter/Jamfile
Normal file
8
src/tests/kits/shared/shake_filter/Jamfile
Normal file
@ -0,0 +1,8 @@
|
||||
SubDir HAIKU_TOP src tests kits shared shake_filter ;
|
||||
|
||||
UsePrivateHeaders shared ;
|
||||
|
||||
Application ShakeFilterTest :
|
||||
ShakeFilterTest.cpp
|
||||
: libshared.a be $(TARGET_LIBSUPC++)
|
||||
;
|
81
src/tests/kits/shared/shake_filter/ShakeFilterTest.cpp
Normal file
81
src/tests/kits/shared/shake_filter/ShakeFilterTest.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2009, Alexandre Deckner <alex@zappotek.com>
|
||||
* 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user