* 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:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user