From 77fa9c0b395a7b755c2dd494c5d367331b201278 Mon Sep 17 00:00:00 2001 From: Alexandre Deckner Date: Sun, 31 Aug 2008 18:40:27 +0000 Subject: [PATCH] * 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 --- src/apps/deskbar/Jamfile | 1 + src/apps/deskbar/LongClickTracker.cpp | 139 ++++++++++++++++++++++++++ src/apps/deskbar/LongClickTracker.h | 52 ++++++++++ src/apps/deskbar/TimeView.cpp | 116 +++++++++------------ src/apps/deskbar/TimeView.h | 5 +- 5 files changed, 244 insertions(+), 69 deletions(-) create mode 100644 src/apps/deskbar/LongClickTracker.cpp create mode 100644 src/apps/deskbar/LongClickTracker.h diff --git a/src/apps/deskbar/Jamfile b/src/apps/deskbar/Jamfile index 22ab22092a..906128ba95 100644 --- a/src/apps/deskbar/Jamfile +++ b/src/apps/deskbar/Jamfile @@ -34,6 +34,7 @@ Application Deskbar : CalendarMenuItem.cpp DeskBarUtils.cpp ExpandoMenuBar.cpp + LongClickTracker.cpp ShowHideMenuItem.cpp StatusView.cpp StatusViewShelf.cpp diff --git a/src/apps/deskbar/LongClickTracker.cpp b/src/apps/deskbar/LongClickTracker.cpp new file mode 100644 index 0000000000..fe28316a04 --- /dev/null +++ b/src/apps/deskbar/LongClickTracker.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2008, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Alexandre Deckner + */ + +#include "LongClickTracker.h" + +#include + + +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(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(); + } + } +} diff --git a/src/apps/deskbar/LongClickTracker.h b/src/apps/deskbar/LongClickTracker.h new file mode 100644 index 0000000000..967dfa2798 --- /dev/null +++ b/src/apps/deskbar/LongClickTracker.h @@ -0,0 +1,52 @@ +/* + * Copyright 2007-2008, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Alexandre Deckner + */ + +/* + * 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 +#include + +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_ diff --git a/src/apps/deskbar/TimeView.cpp b/src/apps/deskbar/TimeView.cpp index 0c1ac3613f..0870ece2c4 100644 --- a/src/apps/deskbar/TimeView.cpp +++ b/src/apps/deskbar/TimeView.cpp @@ -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 } diff --git a/src/apps/deskbar/TimeView.h b/src/apps/deskbar/TimeView.h index 4039e17e8e..e60a5f59ab 100644 --- a/src/apps/deskbar/TimeView.h +++ b/src/apps/deskbar/TimeView.h @@ -34,10 +34,9 @@ All rights reserved. #ifndef TIME_VIEW_H #define TIME_VIEW_H - #include #include - +#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; };