* Extended the Desktop's MouseFilter to reset the mouse-down messages' "clicks"

field. It does that when the modifiers, the pressed buttons, or the click
  target changes between the clicks, or when the distance between the click
  points is >= four pixels.
* Adjusted the Window::MouseDown() and WindowBehavior::MouseDown() interfaces
  and implementation accordingly (we now also pass and return click count and
  click targets).
* Removed the no longer need multi-click handling from DefaultWindowBehaviour.

Fixes #6841 and #6867.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39623 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-11-25 04:03:47 +00:00
parent 76107eeb11
commit 427788e06f
8 changed files with 275 additions and 61 deletions
+80
View File
@@ -0,0 +1,80 @@
/*
* Copyright 2010, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef CLICK_TARGET_H
#define CLICK_TARGET_H
#include <TokenSpace.h>
/*! \brief Identifies a mouse click target in the app server.
Used to discriminate between different targets in order to filter
multi-clicks. A click on a different target resets the click count.
*/
struct ClickTarget {
public:
enum Type {
TYPE_INVALID,
TYPE_WINDOW_CONTENTS,
TYPE_WINDOW_DECORATOR
};
public:
ClickTarget()
:
fType(TYPE_INVALID),
fWindow(B_NULL_TOKEN),
fWindowElement(0)
{
}
ClickTarget(Type type, int32 window, int32 windowElement)
:
fType(type),
fWindow(window),
fWindowElement(windowElement)
{
}
bool IsValid() const
{
return fType != TYPE_INVALID;
}
Type GetType() const
{
return fType;
}
int32 WindowToken() const
{
return fWindow;
}
int32 WindowElement() const
{
return fWindowElement;
}
bool operator==(const ClickTarget& other) const
{
return fType == other.fType && fWindow == other.fWindow
&& fWindowElement == other.fWindowElement;
}
bool operator!=(const ClickTarget& other) const
{
return !(*this == other);
}
private:
Type fType;
int32 fWindow;
int32 fWindowElement;
};
#endif // CLICK_TARGET_H
+13 -38
View File
@@ -19,6 +19,7 @@
#include <WindowPrivate.h>
#include "ClickTarget.h"
#include "Desktop.h"
#include "DrawingEngine.h"
#include "Window.h"
@@ -120,8 +121,7 @@ struct DefaultWindowBehaviour::MouseTrackingState : State {
fMinimizeCheckOnMouseUp = false;
if (message->FindInt32("modifiers") == fBehavior.fLastModifiers
&& (fWindow->Flags() & B_NOT_MINIMIZABLE) == 0
&& system_time() - fLastMoveTime
< kWindowActivationTimeout) {
&& system_time() - fLastMoveTime < kWindowActivationTimeout) {
fWindow->ServerWindow()->NotifyMinimize(true);
}
}
@@ -629,10 +629,7 @@ DefaultWindowBehaviour::DefaultWindowBehaviour(Window* window)
fWindow(window),
fDesktop(window->Desktop()),
fState(NULL),
fLastModifiers(0),
fLastMouseButtons(0),
fLastRegion(REGION_NONE),
fResetClickCount(0)
fLastModifiers(0)
{
}
@@ -644,30 +641,12 @@ DefaultWindowBehaviour::~DefaultWindowBehaviour()
bool
DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where,
int32 lastHitRegion, int32& clickCount, int32& _hitRegion)
{
// Get the click count and reset it, if the modifiers changed in the
// meantime. Do the same when this is not the button we've seen before.
// TODO: At least the modifier check should be done in a better place
// (e.g. the input server). It should also reset clicks after mouse
// movement (which we don't do here either -- though that's probably
// acceptable).
int32 clickCount = message->FindInt32("clicks");
int32 modifiers = message->FindInt32("modifiers");
fLastModifiers = message->FindInt32("modifiers");
int32 buttons = message->FindInt32("buttons");
if (clickCount <= 1) {
fResetClickCount = 0;
} else if (modifiers != fLastModifiers || buttons != fLastMouseButtons
|| clickCount - fResetClickCount < 1) {
fResetClickCount = clickCount - 1;
clickCount = 1;
} else
clickCount -= fResetClickCount;
fLastModifiers = modifiers;
fLastMouseButtons = buttons;
// if a state is active, let it do the job
if (fState != NULL)
return fState->MouseDown(message, where);
@@ -683,7 +662,7 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
if (decorator != NULL)
inBorderRegion = decorator->GetFootprint().Contains(where);
bool windowModifier = _IsWindowModifier(modifiers);
bool windowModifier = _IsWindowModifier(fLastModifiers);
if (windowModifier || inBorderRegion) {
// click on the window decorator or we have the window modifier keys
@@ -712,7 +691,7 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
// tab sliding in any case if either shift key is held down
// except sliding up-down by moving mouse left-right would
// look strange
if ((modifiers & B_SHIFT_KEY) != 0
if ((fLastModifiers & B_SHIFT_KEY) != 0
&& fWindow->Look() != kLeftTitledWindowLook) {
action = ACTION_SLIDE_TAB;
break;
@@ -762,15 +741,7 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
}
}
// The hit region changed since the last the click. Reset the click count.
if (hitRegion != fLastRegion) {
fLastRegion = hitRegion;
clickCount = 1;
fResetClickCount = message->FindInt32("clicks") - 1;
if (fResetClickCount < 0)
fResetClickCount = 0;
}
_hitRegion = (int32)hitRegion;
if (action == ACTION_NONE) {
// No action -- if this is a click inside the window's contents,
@@ -778,6 +749,10 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
return inBorderRegion;
}
// reset the click count, if the hit region differs from the previous one
if (hitRegion != lastHitRegion)
clickCount = 1;
DesktopSettings desktopSettings(fDesktop);
if (!desktopSettings.AcceptFirstClick()) {
// Ignore clicks on decorator buttons if the
+4 -5
View File
@@ -29,7 +29,9 @@ public:
DefaultWindowBehaviour(Window* window);
virtual ~DefaultWindowBehaviour();
virtual bool MouseDown(BMessage* message, BPoint where);
virtual bool MouseDown(BMessage* message, BPoint where,
int32 lastHitRegion, int32& clickCount,
int32& _hitRegion);
virtual void MouseUp(BMessage* message, BPoint where);
virtual void MouseMoved(BMessage *message, BPoint where,
bool isFake);
@@ -38,7 +40,7 @@ public:
private:
enum Region {
REGION_NONE,
REGION_NONE = 0,
REGION_TAB,
REGION_BORDER,
@@ -90,9 +92,6 @@ protected:
Desktop* fDesktop;
State* fState;
int32 fLastModifiers;
int32 fLastMouseButtons;
Region fLastRegion;
int32 fResetClickCount;
};
+97 -8
View File
@@ -36,6 +36,7 @@
#include <WindowInfo.h>
#include "AppServer.h"
#include "ClickTarget.h"
#include "DecorManager.h"
#include "DesktopSettingsPrivate.h"
#include "DrawingEngine.h"
@@ -65,6 +66,20 @@
#endif
static inline float
square_vector_length(float x, float y)
{
return x * x + y * y;
}
static inline float
square_distance(const BPoint& a, const BPoint& b)
{
return square_vector_length(a.x - b.x, a.y - b.y);
}
class KeyboardFilter : public EventFilter {
public:
KeyboardFilter(Desktop* desktop);
@@ -83,14 +98,19 @@ class KeyboardFilter : public EventFilter {
class MouseFilter : public EventFilter {
public:
MouseFilter(Desktop* desktop);
public:
MouseFilter(Desktop* desktop);
virtual filter_result Filter(BMessage* message, EventTarget** _target,
int32* _viewToken, BMessage* latestMouseMoved);
virtual filter_result Filter(BMessage* message, EventTarget** _target,
int32* _viewToken, BMessage* latestMouseMoved);
private:
Desktop* fDesktop;
private:
Desktop* fDesktop;
int32 fLastClickButtons;
int32 fLastClickModifiers;
int32 fResetClickCount;
BPoint fLastClickPoint;
ClickTarget fLastClickTarget;
};
@@ -218,7 +238,12 @@ KeyboardFilter::RemoveTarget(EventTarget* target)
MouseFilter::MouseFilter(Desktop* desktop)
:
fDesktop(desktop)
fDesktop(desktop),
fLastClickButtons(0),
fLastClickModifiers(0),
fResetClickCount(0),
fLastClickPoint(),
fLastClickTarget()
{
}
@@ -248,9 +273,66 @@ MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
// dispatch event to the window
switch (message->what) {
case B_MOUSE_DOWN:
window->MouseDown(message, where, &viewToken);
{
int32 windowToken = window->ServerWindow()->ServerToken();
// First approximation of click count validation. We reset the
// click count when modifiers or pressed buttons have changed
// or when we've got a different click target, or when the
// previous click location is too far from the new one. We can
// only check the window of the click target here; we'll recheck
// after asking the window.
int32 modifiers = message->FindInt32("modifiers");
int32 originalClickCount = message->FindInt32("clicks");
if (originalClickCount <= 0)
originalClickCount = 1;
int32 clickCount = originalClickCount;
if (clickCount > 1) {
if (modifiers != fLastClickModifiers
|| buttons != fLastClickButtons
|| !fLastClickTarget.IsValid()
|| fLastClickTarget.WindowToken() != windowToken
|| square_distance(where, fLastClickPoint) >= 16
|| clickCount - fResetClickCount < 1) {
clickCount = 1;
} else
clickCount -= fResetClickCount;
}
// notify the window
ClickTarget clickTarget;
window->MouseDown(message, where, fLastClickTarget, clickCount,
clickTarget);
// If the click target changed, always reset the click count.
if (clickCount != 1 && clickTarget != fLastClickTarget)
clickCount = 1;
// update our click count management attributes
fResetClickCount = originalClickCount - clickCount;
fLastClickTarget = clickTarget;
fLastClickButtons = buttons;
fLastClickModifiers = modifiers;
fLastClickPoint = where;
// get the view token from the click target
if (clickTarget.GetType() == ClickTarget::TYPE_WINDOW_CONTENTS)
viewToken = clickTarget.WindowElement();
// update the message's "clicks" field, if necessary
if (clickCount != originalClickCount) {
if (message->HasInt32("clicks"))
message->ReplaceInt32("clicks", clickCount);
else
message->AddInt32("clicks", clickCount);
}
// notify desktop listeners
fDesktop->NotifyMouseDown(window, message, where);
break;
}
case B_MOUSE_UP:
window->MouseUp(message, where, &viewToken);
@@ -273,6 +355,13 @@ MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
*_viewToken = viewToken;
*_target = &window->EventTarget();
}
} else if (message->what == B_MOUSE_DOWN) {
// the mouse-down didn't hit a window -- reset the click target
fResetClickCount = 0;
fLastClickTarget = ClickTarget();
fLastClickButtons = message->FindInt32("buttons");
fLastClickModifiers = message->FindInt32("modifiers");
fLastClickPoint = where;
}
if (window == NULL || viewToken == B_NULL_TOKEN) {
+45 -7
View File
@@ -14,6 +14,7 @@
#include "Window.h"
#include "ClickTarget.h"
#include "Decorator.h"
#include "DecorManager.h"
#include "Desktop.h"
@@ -781,14 +782,47 @@ Window::EnableUpdateRequests()
// #pragma mark -
void
Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken)
{
DesktopSettings desktopSettings(fDesktop);
/*! \brief Handles a mouse-down message for the window.
bool eventEaten = fWindowBehaviour->MouseDown(message, where);
if (!eventEaten) {
\param message The message.
\param where The point where the mouse click happened.
\param lastClickTarget The target of the previous click.
\param clickCount The number of subsequent, no longer than double-click
interval separated clicks that have happened so far. This number doesn't
necessarily match the value in the message. It has already been
pre-processed in order to avoid erroneous multi-clicks (e.g. when a
different button has been used or a different window was targeted). This
is an in-out variable. The method can reset the value to 1, if it
doesn't want this event handled as a multi-click. Returning a different
click target will also make the caller reset the click count.
\param _clickTarget Set by the method to a value identifying the clicked
element. If not explicitly set, an invalid click target is assumed.
*/
void
Window::MouseDown(BMessage* message, BPoint where,
const ClickTarget& lastClickTarget, int32& clickCount,
ClickTarget& _clickTarget)
{
// If the previous click hit our decorator, get the hit region.
int32 windowToken = fWindow->ServerToken();
int32 lastHitRegion = 0;
if (lastClickTarget.GetType() == ClickTarget::TYPE_WINDOW_DECORATOR
&& lastClickTarget.WindowToken() == windowToken) {
lastHitRegion = lastClickTarget.WindowElement();
}
// Let the window behavior process the mouse event.
int32 hitRegion = 0;
bool eventEaten = fWindowBehaviour->MouseDown(message, where, lastHitRegion,
clickCount, hitRegion);
if (eventEaten) {
// click on the decorator (or equivalent)
_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_DECORATOR,
windowToken, (int32)hitRegion);
} else {
// click was inside the window contents
int32 viewToken = B_NULL_TOKEN;
if (View* view = ViewAt(where)) {
if (HasModal())
return;
@@ -801,6 +835,7 @@ Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken)
// Activate or focus the window in case it doesn't accept first
// click, depending on the mouse mode
DesktopSettings desktopSettings(fDesktop);
if (desktopSettings.MouseMode() == B_NORMAL_MOUSE
&& !acceptFirstClick)
fDesktop->ActivateWindow(this);
@@ -819,9 +854,12 @@ Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken)
}
// fill out view token for the view under the mouse
*_viewToken = view->Token();
viewToken = view->Token();
view->MouseDown(message, where);
}
_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_CONTENTS,
windowToken, viewToken);
}
}
+4 -1
View File
@@ -27,6 +27,7 @@ namespace BPrivate {
class PortLink;
};
class ClickTarget;
class ClientLooper;
class Decorator;
class Desktop;
@@ -144,7 +145,9 @@ public:
int32 xOffset, int32 yOffset);
void MouseDown(BMessage* message, BPoint where,
int32* _viewToken);
const ClickTarget& lastClickTarget,
int32& clickCount,
ClickTarget& _clickTarget);
void MouseUp(BMessage* message, BPoint where,
int32* _viewToken);
void MouseMoved(BMessage* message, BPoint where,
+26
View File
@@ -27,3 +27,29 @@ void
WindowBehaviour::ModifiersChanged(int32 modifiers)
{
}
/*! \fn WindowBehaviour::MouseDown()
\brief Handles a mouse-down message for the window.
Note that values passed and returned for the hit regions are only meaningful
to the WindowBehavior subclass, save for the value 0, which is refers to an
invalid region.
\param message The message.
\param where The point where the mouse click happened.
\param lastHitRegion The hit region of the previous click.
\param clickCount The number of subsequent, no longer than double-click
interval separated clicks that have happened so far. This number doesn't
necessarily match the value in the message. It has already been
pre-processed in order to avoid erroneous multi-clicks (e.g. when a
different button has been used or a different window was targeted). This
is an in-out variable. The method can reset the value to 1, if it
doesn't want this event handled as a multi-click. Returning a different
click hit region will also make the caller reset the click count.
\param _hitRegion Set by the method to a value identifying the clicked
decorator element. If not explicitly set, an invalid hit region (0) is
assumed. Only needs to be set when returning \c true.
\return \c true, if the event was a WindowBehaviour event and should be
discarded.
*/
+6 -2
View File
@@ -11,8 +11,11 @@
#include <Region.h>
#include "Decorator.h"
class BMessage;
class ClickTarget;
class WindowBehaviour {
@@ -20,8 +23,9 @@ public:
WindowBehaviour();
virtual ~WindowBehaviour();
//! \return true if event was a WindowBehaviour event and should be discard
virtual bool MouseDown(BMessage* message, BPoint where) = 0;
virtual bool MouseDown(BMessage* message, BPoint where,
int32 lastHitRegion, int32& clickCount,
int32& _hitRegion) = 0;
virtual void MouseUp(BMessage* message, BPoint where) = 0;
virtual void MouseMoved(BMessage *message, BPoint where,
bool isFake) = 0;