* Rewrote long click mouse tracking. This avoids busy looping in TimeView::MouseDown and fixes

#591. The helper class could be reused for similar situations (ex: Tracker context menu and 
drag detection). See the brief doc in the code. Comments welcomed!


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27258 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Alexandre Deckner
2008-08-31 18:40:27 +00:00
parent 6d3973a54a
commit 77fa9c0b39
5 changed files with 244 additions and 69 deletions
+1
View File
@@ -34,6 +34,7 @@ Application Deskbar :
CalendarMenuItem.cpp
DeskBarUtils.cpp
ExpandoMenuBar.cpp
LongClickTracker.cpp
ShowHideMenuItem.cpp
StatusView.cpp
StatusViewShelf.cpp
+139
View File
@@ -0,0 +1,139 @@
/*
* Copyright 2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexandre Deckner <[email protected]>
*/
#include "LongClickTracker.h"
#include <View.h>
LongClickTracker::LongClickTracker(BView *view, uint32 messageWhat)
: fView(view),
fMessenger(NULL),
fMessageWhat(messageWhat),
fThread(B_ERROR),
fQuit(false)
{
// use the doubleClickSpeed as a threshold
get_click_speed(&fLongClickThreshold);
}
LongClickTracker::~LongClickTracker()
{
if (fThread != B_NO_ERROR)
return;
fQuit = true;
status_t ret;
wait_for_thread(fThread, &ret);
delete fMessenger;
}
// Must be called _after_ the view has been attached to a window
status_t
LongClickTracker::Start()
{
status_t err;
if (!fView->Window())
return B_ERROR;
fMessenger = new BMessenger(fView, NULL, &err);
if (err != B_OK)
return err;
fThread = spawn_thread(LongClickTracker::_ThreadEntry, "clickTracker",
B_NORMAL_PRIORITY, this);
err = resume_thread(fThread);
if (err != B_NO_ERROR) {
kill_thread(fThread);
fThread = B_ERROR;
} else {
fQuit = false;
}
return err;
}
int32
LongClickTracker::_ThreadEntry(void *pointer)
{
LongClickTracker *that = reinterpret_cast<LongClickTracker*>(pointer);
that->_Track();
return B_OK;
}
void
LongClickTracker::_Track()
{
uint32 buttons;
BPoint position;
bool timing = false;
//when true, we are currently timing the last click duration
bool ready = true;
//when true, button has been released, we can start timing on next click
bigtime_t clickTime = 0;
BRect bounds;
fView->LockLooper();
bounds = fView->Bounds();
fView->UnlockLooper();
while (!fQuit) {
snooze(20000);
if (fView->Window()) {
fView->LockLooper();
fView->GetMouse(&position, &buttons, false);
fView->UnlockLooper();
}
if (timing) {
if (!bounds.Contains(position)) {
//mouse exited the view, stop timing
timing = false;
ready = false; //not ready yet, the button might be still down
}
if (buttons != B_PRIMARY_MOUSE_BUTTON) {
//button has been released, stop timing, set ready
timing = false;
ready = true;
} else if ((system_time() - clickTime) > fLongClickThreshold){
BMessage message(fMessageWhat);
message.AddPoint("where", position);
fMessenger->SendMessage(&message);
timing = false;
ready = false;
}
}
if (!ready && !timing && (buttons != B_PRIMARY_MOUSE_BUTTON)) {
//mouse released, ready to time on next click
ready = true;
}
if (ready && !timing && buttons == B_PRIMARY_MOUSE_BUTTON
&& bounds.Contains(position)) {
//mouse clicked in view, start timing
timing = true;
clickTime = system_time();
}
}
}
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexandre Deckner <[email protected]>
*/
/*
* LongClickTracker:
* This class provides a way to track long clicks asynchronously.
* Set the view to track and the code of the message you want to receive.
* When a long click occurs in the view, a message will be sent to the view.
* It contains the coordinates of the click (last postion) in a BPoint:"where"
*
* Could be extended to be more parametrable and/or support other kind of
* complex mouse tracking. This could also be done on the input server side
* eventually, and use this class for R5 backward compatibility.
*
* Call Start() after the view has been attached to a window.
*/
#ifndef _LONG_CLICK_TRACKER_H_
#define _LONG_CLICK_TRACKER_H_
#include <Messenger.h>
#include <OS.h>
class BView;
class BMessenger;
class LongClickTracker
{
public:
LongClickTracker(BView *view, uint32 messageWhat);
~LongClickTracker();
status_t Start();
protected:
static int32 _ThreadEntry(void *that);
void _Track();
BView* fView;
BMessenger* fMessenger;
int32 fMessageWhat;
thread_id fThread;
bool fQuit;
bigtime_t fLongClickThreshold;
};
#endif // _LONG_CLICK_TRACKER_H_
+49 -67
View File
@@ -65,7 +65,8 @@ static const float kHMargin = 2.0;
enum {
kMsgShowClock,
kMsgChangeClock,
kMsgHide
kMsgHide,
kMsgLongClick
};
@@ -82,7 +83,8 @@ TTimeView::TTimeView(float maxWidth, float height, bool showSeconds, bool milTim
fEuroDate(euroDate),
fMaxWidth(maxWidth),
fHeight(height),
fOrientation(true)
fOrientation(true),
fLongClickTracker(this, kMsgLongClick)
{
fShowingDate = false;
fTime = fLastTime = time(NULL);
@@ -154,6 +156,8 @@ TTimeView::AttachedToWindow()
ResizeToPreferred();
CalculateTextPlacement();
fLongClickTracker.Start();
}
@@ -230,6 +234,49 @@ TTimeView::MessageReceived(BMessage* message)
case 'time':
Window()->PostMessage(message, Parent());
break;
case kMsgLongClick:
{
BPoint where;
message->FindPoint("where", &where);
//TODO: do nothing if the calendar is already shown
#ifdef _SHOW_CALENDAR_MENU_ITEM
BPopUpMenu *menu = new BPopUpMenu("", false, false);
menu->SetFont(be_plain_font);
menu->AddItem(new CalendarMenuItem());
menu->ResizeToPreferred();
BPoint point = where;
BScreen screen;
where.y = Bounds().bottom + 4;
// make sure the menu is visible and doesn't hide the date
ConvertToScreen(&where);
if (where.y + menu->Bounds().Height() > screen.Frame().bottom)
where.y -= menu->Bounds().Height() + 2 * Bounds().Height();
ConvertToScreen(&point);
menu->Go(where, true, true, BRect(point.x - 4, point.y - 4,
point.x + 4, point.y + 4), true);
#elif _SHOW_CALENDAR_MENU_WINDOW
where.y = Bounds().bottom + 4.0;
ConvertToScreen(&where);
if (where.y >= BScreen().Frame().bottom)
where.y -= (Bounds().Height() + 4.0);
CalendarMenuWindow* window = new CalendarMenuWindow(where, fEuroDate);
window->Show();
#endif
break;
}
default:
BView::MessageReceived(message);
@@ -326,71 +373,6 @@ TTimeView::MouseDown(BPoint point)
fLastDateStr[0] = '\0';
fLastTimeStr[0] = '\0';
Pulse();
#ifdef _SHOW_CALENDAR_MENU_ITEM
// see if the user holds down the button long enough to show him the calendar
bigtime_t startTime = system_time();
// use the doubleClickSpeed as a treshold
bigtime_t doubleClickSpeed;
get_click_speed(&doubleClickSpeed);
while (buttons) {
BPoint where;
GetMouse(&where, &buttons, false);
if ((system_time() - startTime) > doubleClickSpeed) {
BPopUpMenu *menu = new BPopUpMenu("", false, false);
menu->SetFont(be_plain_font);
menu->AddItem(new CalendarMenuItem());
menu->ResizeToPreferred();
point = where;
BScreen screen;
where.y = Bounds().bottom + 4;
// make sure the menu is visible at doesn't hide the date
ConvertToScreen(&where);
if (where.y + menu->Bounds().Height() > screen.Frame().bottom)
where.y -= menu->Bounds().Height() + 2 * Bounds().Height();
ConvertToScreen(&point);
menu->Go(where, true, true, BRect(point.x - 4, point.y - 4,
point.x + 4, point.y + 4), true);
return;
}
snooze(15000);
}
#elif _SHOW_CALENDAR_MENU_WINDOW
bigtime_t startTime = system_time();
// use the doubleClickSpeed as a treshold
bigtime_t doubleClickSpeed;
get_click_speed(&doubleClickSpeed);
while (buttons) {
BPoint where;
GetMouse(&where, &buttons, false);
if ((system_time() - startTime) > doubleClickSpeed) {
where.y = Bounds().bottom + 4.0;
ConvertToScreen(&where);
if (where.y >= BScreen().Frame().bottom)
where.y -= (Bounds().Height() + 4.0);
CalendarMenuWindow* window = new CalendarMenuWindow(where, fEuroDate);
window->Show();
return;
}
snooze(15000);
}
#endif
}
+3 -2
View File
@@ -34,10 +34,9 @@ All rights reserved.
#ifndef TIME_VIEW_H
#define TIME_VIEW_H
#include <OS.h>
#include <View.h>
#include "LongClickTracker.h"
const uint32 kMsgShowSeconds = 'ShSc';
const uint32 kMsgMilTime = 'MilT';
@@ -124,6 +123,8 @@ class TTimeView : public BView {
bool fOrientation; // vertical = true
BPoint fTimeLocation;
BPoint fDateLocation;
LongClickTracker fLongClickTracker;
};