* Refactored the MouseMoved(), MouseUp(), and part of the MouseDown() code

into newly introduced state classes.
* Fixed the right-click-while dragging behavior, I broke in r39602.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39608 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-11-24 16:17:32 +00:00
parent fb4527d22e
commit 52cac2e7a4
2 changed files with 524 additions and 358 deletions
+506 -344
View File
@@ -34,51 +34,484 @@
static const bigtime_t kWindowActivationTimeout = 500000LL;
// #pragma mark - State
struct DefaultWindowBehaviour::State {
State(DefaultWindowBehaviour& behavior)
:
fBehavior(behavior),
fWindow(behavior.fWindow),
fDesktop(behavior.fDesktop)
{
}
virtual ~State()
{
}
virtual void EnterState(State* previousState)
{
}
virtual bool MouseDown(BMessage* message, BPoint where)
{
return false;
}
virtual void MouseUp(BMessage* message, BPoint where)
{
}
virtual void MouseMoved(BMessage* message, BPoint where, bool isFake)
{
}
void UpateFFMFocus(bool isFake)
{
// change focus in FFM mode
DesktopSettings desktopSettings(fDesktop);
if (desktopSettings.FocusFollowsMouse()
&& !fWindow->IsFocus() && !(fWindow->Flags() & B_AVOID_FOCUS)) {
// If the mouse move is a fake one, we set the focus to NULL, which
// will cause the window that had focus last to retrieve it again - this
// makes FFM much nicer to use with the keyboard.
fDesktop->SetFocusWindow(isFake ? NULL : fWindow);
}
}
protected:
DefaultWindowBehaviour& fBehavior;
Window* fWindow;
Desktop* fDesktop;
};
// #pragma mark - MouseTrackingState
struct DefaultWindowBehaviour::MouseTrackingState : State {
MouseTrackingState(DefaultWindowBehaviour& behavior, BPoint where,
bool activateOnMouseUp, bool minimizeCheckOnMouseUp)
:
State(behavior),
fActivateOnMouseUp(activateOnMouseUp),
fMinimizeCheckOnMouseUp(minimizeCheckOnMouseUp),
fLastMousePosition(where),
fMouseMoveDistance(0),
fLastMoveTime(system_time())
{
}
virtual void MouseUp(BMessage* message, BPoint where)
{
// ignore, if it's not the primary mouse button
int32 buttons = message->FindInt32("buttons");
if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0)
return;
if (fMinimizeCheckOnMouseUp) {
// If the modifiers haven't changed in the meantime and not too
// much time has elapsed, we're supposed to minimize the window.
fMinimizeCheckOnMouseUp = false;
if (message->FindInt32("modifiers") == fBehavior.fLastModifiers
&& (fWindow->Flags() & B_NOT_MINIMIZABLE) == 0
&& system_time() - fLastMoveTime
< kWindowActivationTimeout) {
fWindow->ServerWindow()->NotifyMinimize(true);
}
}
// In FFM mode, activate the window and bring it to front in case
// this was a drag click but the mouse was not moved.
if (fActivateOnMouseUp) {
fActivateOnMouseUp = false;
// on R5, there is a time window for this feature
// ie, click and press too long, nothing will happen
if (system_time() - fLastMoveTime < kWindowActivationTimeout)
fDesktop->ActivateWindow(fWindow);
}
fBehavior._NextState(NULL);
}
virtual void MouseMoved(BMessage* message, BPoint where, bool isFake)
{
// Limit the rate at which "mouse moved" events are handled that move
// or resize the window. At the moment this affects also tab sliding,
// but 1/75 s is a pretty fine granularity anyway, so don't bother.
bigtime_t now = system_time();
if (now - fLastMoveTime < 13333) {
// TODO: add a "timed event" to query for
// the then current mouse position
return;
}
if (fActivateOnMouseUp || fMinimizeCheckOnMouseUp) {
if (now - fLastMoveTime >= kWindowActivationTimeout) {
// This click is too long already for window activation/
// minimizing.
fActivateOnMouseUp = false;
fMinimizeCheckOnMouseUp = false;
fLastMoveTime = now;
}
} else
fLastMoveTime = now;
BPoint delta = where - fLastMousePosition;
// NOTE: "delta" is later used to change fLastMousePosition.
// If for some reason no change should take effect, delta
// is to be set to (0, 0) so that fLastMousePosition is not
// adjusted. This way the relative mouse position to the
// item being changed (border during resizing, tab during
// sliding...) stays fixed when the mouse is moved so that
// changes are taking effect again.
// If the window was moved enough, it doesn't come to
// the front in FFM mode when the mouse is released.
if (fActivateOnMouseUp || fMinimizeCheckOnMouseUp) {
fMouseMoveDistance += delta.x * delta.x + delta.y * delta.y;
if (fMouseMoveDistance > 16.0f) {
fActivateOnMouseUp = false;
fMinimizeCheckOnMouseUp = false;
} else
delta = B_ORIGIN;
}
// perform the action (this also updates the delta)
MouseMovedAction(delta, now);
// set the new mouse position
fLastMousePosition += delta;
// update the FFM focus
UpateFFMFocus(isFake);
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now) = 0;
protected:
bool fActivateOnMouseUp : 1;
bool fMinimizeCheckOnMouseUp : 1;
BPoint fLastMousePosition;
float fMouseMoveDistance;
bigtime_t fLastMoveTime;
};
// #pragma mark - DragState
struct DefaultWindowBehaviour::DragState : MouseTrackingState {
DragState(DefaultWindowBehaviour& behavior, BPoint where,
bool activateOnMouseUp, bool minimizeCheckOnMouseUp)
:
MouseTrackingState(behavior, where, activateOnMouseUp,
minimizeCheckOnMouseUp)
{
}
virtual bool MouseDown(BMessage* message, BPoint where)
{
// right-click while dragging shall bring the window to front
int32 buttons = message->FindInt32("buttons");
if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
if (fWindow == fDesktop->BackWindow())
fDesktop->ActivateWindow(fWindow);
else
fDesktop->SendWindowBehind(fWindow);
return false;
}
return MouseTrackingState::MouseDown(message, where);
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now)
{
if (!(fWindow->Flags() & B_NOT_MOVABLE)) {
BPoint oldLeftTop = fWindow->Frame().LeftTop();
_AlterDeltaForSnap(delta, now);
fDesktop->MoveWindowBy(fWindow, delta.x, delta.y);
// constrain delta to true change in position
delta = fWindow->Frame().LeftTop() - oldLeftTop;
} else
delta = BPoint(0, 0);
}
private:
void _AlterDeltaForSnap(BPoint& delta, bigtime_t now)
{
// Alter the delta (which is a proposed offset used while dragging a
// window) so that the frame of the window 'snaps' to the edges of the
// screen.
const bigtime_t kSnappingDuration = 1500000LL;
const bigtime_t kSnappingPause = 3000000LL;
const float kSnapDistance = 8.0f;
if (now - fLastSnapTime > kSnappingDuration
&& now - fLastSnapTime < kSnappingPause) {
// Maintain a pause between snapping.
return;
}
BRect frame = fWindow->Frame();
BPoint offsetWithinFrame;
// TODO: Perhaps obtain the usable area (not covered by the Deskbar)?
BRect screenFrame = fWindow->Screen()->Frame();
Decorator* decorator = fWindow->Decorator();
if (decorator) {
frame = decorator->GetFootprint().Frame();
offsetWithinFrame.x = fWindow->Frame().left - frame.left;
offsetWithinFrame.y = fWindow->Frame().top - frame.top;
}
frame.OffsetBy(delta);
float leftDist = fabs(frame.left - screenFrame.left);
float topDist = fabs(frame.top - screenFrame.top);
float rightDist = fabs(frame.right - screenFrame.right);
float bottomDist = fabs(frame.bottom - screenFrame.bottom);
bool snapped = false;
if (leftDist < kSnapDistance || rightDist < kSnapDistance) {
snapped = true;
if (leftDist < rightDist) {
frame.right -= frame.left;
frame.left = 0.0f;
} else {
frame.left -= frame.right - screenFrame.right;
frame.right = screenFrame.right;
}
}
if (topDist < kSnapDistance || bottomDist < kSnapDistance) {
snapped = true;
if (topDist < bottomDist) {
frame.bottom -= frame.top;
frame.top = 0.0f;
} else {
frame.top -= frame.bottom - screenFrame.bottom;
frame.bottom = screenFrame.bottom;
}
}
if (snapped && now - fLastSnapTime > kSnappingPause)
fLastSnapTime = now;
frame.top += offsetWithinFrame.y;
frame.left += offsetWithinFrame.x;
delta.y = frame.top - fWindow->Frame().top;
delta.x = frame.left - fWindow->Frame().left;
}
private:
bigtime_t fLastSnapTime;
};
// #pragma mark - ResizeState
struct DefaultWindowBehaviour::ResizeState : MouseTrackingState {
ResizeState(DefaultWindowBehaviour& behavior, BPoint where,
bool activateOnMouseUp)
:
MouseTrackingState(behavior, where, activateOnMouseUp, false)
{
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now)
{
if (!(fWindow->Flags() & B_NOT_RESIZABLE)) {
if (fWindow->Flags() & B_NOT_V_RESIZABLE)
delta.y = 0;
if (fWindow->Flags() & B_NOT_H_RESIZABLE)
delta.x = 0;
BPoint oldRightBottom = fWindow->Frame().RightBottom();
fDesktop->ResizeWindowBy(fWindow, delta.x, delta.y);
// constrain delta to true change in size
delta = fWindow->Frame().RightBottom() - oldRightBottom;
} else
delta = BPoint(0, 0);
}
};
// #pragma mark - SlideTabState
struct DefaultWindowBehaviour::SlideTabState : MouseTrackingState {
SlideTabState(DefaultWindowBehaviour& behavior, BPoint where)
:
MouseTrackingState(behavior, where, false, false)
{
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now)
{
float loc = fWindow->TabLocation();
// TODO: change to [0:1]
loc += delta.x;
if (fDesktop->SetWindowTabLocation(fWindow, loc))
delta.y = 0;
else
delta = BPoint(0, 0);
}
};
// #pragma mark - DecoratorButtonState
struct DefaultWindowBehaviour::DecoratorButtonState : State {
DecoratorButtonState(DefaultWindowBehaviour& behavior, Region button)
:
State(behavior),
fButton(button)
{
}
virtual void EnterState(State* previousState)
{
_RedrawDecorator(NULL);
}
virtual void MouseUp(BMessage* message, BPoint where)
{
// ignore, if it's not the primary mouse button
int32 buttons = message->FindInt32("buttons");
if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0)
return;
// redraw the decorator
if (Decorator* decorator = fWindow->Decorator()) {
BRegion* visibleBorder = fWindow->RegionPool()->GetRegion();
fWindow->GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&fWindow->VisibleRegion());
DrawingEngine* engine = decorator->GetDrawingEngine();
engine->LockParallelAccess();
engine->ConstrainClippingRegion(visibleBorder);
switch (fButton) {
case REGION_CLOSE_BUTTON:
decorator->SetClose(false);
if (fBehavior._RegionFor(message) == REGION_CLOSE_BUTTON)
fWindow->ServerWindow()->NotifyQuitRequested();
break;
case REGION_ZOOM_BUTTON:
decorator->SetZoom(false);
if (fBehavior._RegionFor(message) == REGION_ZOOM_BUTTON)
fWindow->ServerWindow()->NotifyZoom();
break;
case REGION_MINIMIZE_BUTTON:
decorator->SetMinimize(false);
if (fBehavior._RegionFor(message) == REGION_MINIMIZE_BUTTON)
fWindow->ServerWindow()->NotifyMinimize(true);
break;
default:
break;
}
engine->UnlockParallelAccess();
fWindow->RegionPool()->Recycle(visibleBorder);
}
fBehavior._NextState(NULL);
}
virtual void MouseMoved(BMessage* message, BPoint where, bool isFake)
{
_RedrawDecorator(message);
// update the FFM focus
UpateFFMFocus(isFake);
}
private:
void _RedrawDecorator(const BMessage* message)
{
if (Decorator* decorator = fWindow->Decorator()) {
BRegion* visibleBorder = fWindow->RegionPool()->GetRegion();
fWindow->GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&fWindow->VisibleRegion());
DrawingEngine* engine = decorator->GetDrawingEngine();
engine->LockParallelAccess();
engine->ConstrainClippingRegion(visibleBorder);
Region hitRegion = message != NULL
? fBehavior._RegionFor(message) : fButton;
switch (fButton) {
case REGION_CLOSE_BUTTON:
decorator->SetClose(hitRegion == REGION_CLOSE_BUTTON);
break;
case REGION_ZOOM_BUTTON:
decorator->SetZoom(hitRegion == REGION_ZOOM_BUTTON);
break;
case REGION_MINIMIZE_BUTTON:
decorator->SetMinimize(hitRegion == REGION_MINIMIZE_BUTTON);
break;
default:
break;
}
engine->UnlockParallelAccess();
fWindow->RegionPool()->Recycle(visibleBorder);
}
}
protected:
Region fButton;
};
// #pragma mark - DefaultWindowBehaviour
DefaultWindowBehaviour::DefaultWindowBehaviour(Window* window)
:
fWindow(window),
fIsClosing(false),
fIsMinimizing(false),
fIsZooming(false),
fIsSlidingTab(false),
fActivateOnMouseUp(false),
fMinimizeCheckOnMouseUp(false),
fLastMousePosition(0.0f, 0.0f),
fMouseMoveDistance(0.0f),
fLastMoveTime(0),
fLastSnapTime(0),
fDesktop(window->Desktop()),
fState(NULL),
fLastModifiers(0),
fResetClickCount(0)
{
fDesktop = fWindow->Desktop();
}
DefaultWindowBehaviour::~DefaultWindowBehaviour()
{
delete fState;
}
bool
DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
{
Decorator* decorator = fWindow->Decorator();
bool inBorderRegion = false;
if (decorator != NULL)
inBorderRegion = decorator->GetFootprint().Contains(where);
int32 modifiers = message->FindInt32("modifiers");
bool windowModifier = _IsWindowModifier(modifiers);
// Get the click count and reset it, if the modifiers changed in the
// meantime.
// TODO: This 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");
if (clickCount <= 1) {
fResetClickCount = 0;
} else if (modifiers != fLastModifiers
@@ -89,9 +522,23 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
clickCount -= fResetClickCount;
fLastModifiers = modifiers;
// if a state is active, let it do the job
if (fState != NULL)
return fState->MouseDown(message, where);
// No state active yet -- determine the click region and decide what to do.
Decorator* decorator = fWindow->Decorator();
Region hitRegion = REGION_NONE;
click_type action = CLICK_NONE;
bool inBorderRegion = false;
if (decorator != NULL)
inBorderRegion = decorator->GetFootprint().Contains(where);
bool windowModifier = _IsWindowModifier(modifiers);
if (windowModifier || inBorderRegion) {
// click on the window decorator or we have the window modifier keys
// held
@@ -184,94 +631,57 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
action = CLICK_DRAG;
}
// set decorator internals
bool activateOnMouseUp = false;
if (action != CLICK_MOVE_TO_BACK) {
// activate window if in click to activate mode, else only focus it
if (desktopSettings.MouseMode() == B_NORMAL_MOUSE) {
fDesktop->ActivateWindow(fWindow);
} else {
fDesktop->SetFocusWindow(fWindow);
activateOnMouseUp = true;
}
}
// switch to the new state
switch (action) {
case CLICK_CLOSE:
fIsClosing = true;
STRACE_CLICK(("===> CLICK_CLOSE\n"));
break;
case CLICK_ZOOM:
fIsZooming = true;
STRACE_CLICK(("===> CLICK_ZOOM\n"));
break;
case CLICK_MINIMIZE:
if ((fWindow->Flags() & B_NOT_MINIMIZABLE) == 0) {
fIsMinimizing = true;
STRACE_CLICK(("===> CLICK_MINIMIZE\n"));
}
_NextState(
new (std::nothrow) DecoratorButtonState(*this, hitRegion));
STRACE_CLICK(("===> CLICK_CLOSE/ZOOM/MINIMIZE\n"));
break;
case CLICK_DRAG:
fIsDragging = true;
fLastMousePosition = where;
_NextState(new (std::nothrow) DragState(*this, where,
activateOnMouseUp, clickCount == 2));
STRACE_CLICK(("===> CLICK_DRAG\n"));
break;
case CLICK_RESIZE:
fIsResizing = true;
fLastMousePosition = where;
_NextState(new (std::nothrow) ResizeState(*this, where,
activateOnMouseUp));
STRACE_CLICK(("===> CLICK_RESIZE\n"));
break;
case CLICK_SLIDE_TAB:
fIsSlidingTab = true;
fLastMousePosition = where;
_NextState(new (std::nothrow) SlideTabState(*this, where));
STRACE_CLICK(("===> CLICK_SLIDE_TAB\n"));
break;
case CLICK_MOVE_TO_BACK:
fDesktop->SendWindowBehind(fWindow);
STRACE_CLICK(("===> CLICK_MOVE_TO_BACK\n"));
break;
default:
break;
}
if (decorator != NULL) {
// redraw decorator
BRegion* visibleBorder = fWindow->RegionPool()->GetRegion();
fWindow->GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&fWindow->VisibleRegion());
DrawingEngine* engine = decorator->GetDrawingEngine();
engine->LockParallelAccess();
engine->ConstrainClippingRegion(visibleBorder);
if (fIsZooming)
decorator->SetZoom(true);
else if (fIsClosing)
decorator->SetClose(true);
else if (fIsMinimizing)
decorator->SetMinimize(true);
engine->UnlockParallelAccess();
fWindow->RegionPool()->Recycle(visibleBorder);
}
if (action == CLICK_MOVE_TO_BACK) {
if (!fIsDragging || fWindow != fDesktop->BackWindow())
fDesktop->SendWindowBehind(fWindow);
else
fDesktop->ActivateWindow(fWindow);
} else {
// If we have a state now, it surely wants all further mouse events.
if (fState != NULL)
fDesktop->SetMouseEventWindow(fWindow);
// activate window if in click to activate mode, else only focus it
if (desktopSettings.MouseMode() == B_NORMAL_MOUSE)
fDesktop->ActivateWindow(fWindow);
else {
fDesktop->SetFocusWindow(fWindow);
if (action == CLICK_DRAG || action == CLICK_RESIZE)
fActivateOnMouseUp = true;
}
if (fIsDragging && clickCount == 2)
fMinimizeCheckOnMouseUp = true;
fMouseMoveDistance = 0.0f;
fLastMoveTime = system_time();
}
return true;
}
@@ -279,207 +689,16 @@ DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where)
void
DefaultWindowBehaviour::MouseUp(BMessage* message, BPoint where)
{
Decorator* decorator = fWindow->Decorator();
int32 buttons = message->FindInt32("buttons");
if (decorator != NULL) {
// redraw decorator
BRegion* visibleBorder = fWindow->RegionPool()->GetRegion();
fWindow->GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&fWindow->VisibleRegion());
DrawingEngine* engine = decorator->GetDrawingEngine();
engine->LockParallelAccess();
engine->ConstrainClippingRegion(visibleBorder);
if (fIsZooming) {
fIsZooming = false;
decorator->SetZoom(false);
if (_RegionFor(message) == REGION_ZOOM_BUTTON)
fWindow->ServerWindow()->NotifyZoom();
}
if (fIsClosing) {
fIsClosing = false;
decorator->SetClose(false);
if (_RegionFor(message) == REGION_CLOSE_BUTTON)
fWindow->ServerWindow()->NotifyQuitRequested();
}
if (fIsMinimizing) {
fIsMinimizing = false;
decorator->SetMinimize(false);
if (_RegionFor(message) == REGION_MINIMIZE_BUTTON)
fWindow->ServerWindow()->NotifyMinimize(true);
}
engine->UnlockParallelAccess();
fWindow->RegionPool()->Recycle(visibleBorder);
}
// if the primary mouse button is released, stop
// dragging/resizing/sliding
if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0) {
if (fMinimizeCheckOnMouseUp) {
// If the modifiers haven't changed in the meantime and not too
// much time has elapsed, we're supposed to minimize the window.
fMinimizeCheckOnMouseUp = false;
if (message->FindInt32("modifiers") == fLastModifiers
&& (fWindow->Flags() & B_NOT_MINIMIZABLE) == 0
&& system_time() - fLastMoveTime < kWindowActivationTimeout) {
fWindow->ServerWindow()->NotifyMinimize(true);
}
}
// In FFM mode, activate the window and bring it to front in case this
// was a drag click but the mouse was not moved.
if (fActivateOnMouseUp) {
fActivateOnMouseUp = false;
// on R5, there is a time window for this feature
// ie, click and press too long, nothing will happen
if (system_time() - fLastMoveTime < kWindowActivationTimeout)
fDesktop->ActivateWindow(fWindow);
}
fIsDragging = false;
fIsResizing = false;
fIsSlidingTab = false;
}
if (fState != NULL)
fState->MouseUp(message, where);
}
void
DefaultWindowBehaviour::MouseMoved(BMessage *message, BPoint where, bool isFake)
{
Decorator* decorator = fWindow->Decorator();
#if 0
if (decorator != NULL && fWindow->TopView() != NULL) {
DrawingEngine* engine = decorator->GetDrawingEngine();
engine->LockParallelAccess();
engine->ConstrainClippingRegion(&fWindow->VisibleRegion());
fWindow->TopView()->MarkAt(engine, where);
engine->UnlockParallelAccess();
}
#endif
// limit the rate at which "mouse moved" events
// are handled that move or resize the window
bigtime_t now = 0;
if (fIsDragging || fIsResizing) {
now = system_time();
if (now - fLastMoveTime < 13333) {
// TODO: add a "timed event" to query for
// the then current mouse position
return;
}
if (fActivateOnMouseUp || fMinimizeCheckOnMouseUp) {
if (now - fLastMoveTime >= kWindowActivationTimeout) {
// This click is too long already for window activation/
// minimizing.
fActivateOnMouseUp = false;
fMinimizeCheckOnMouseUp = false;
}
} else
fLastMoveTime = now;
}
if (decorator != NULL) {
BRegion* visibleBorder = fWindow->RegionPool()->GetRegion();
fWindow->GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&fWindow->VisibleRegion());
DrawingEngine* engine = decorator->GetDrawingEngine();
engine->LockParallelAccess();
engine->ConstrainClippingRegion(visibleBorder);
Region hitRegion = _RegionFor(message);
if (fIsZooming)
decorator->SetZoom(hitRegion == REGION_ZOOM_BUTTON);
else if (fIsClosing)
decorator->SetClose(hitRegion == REGION_CLOSE_BUTTON);
else if (fIsMinimizing)
decorator->SetMinimize(hitRegion == REGION_MINIMIZE_BUTTON);
engine->UnlockParallelAccess();
fWindow->RegionPool()->Recycle(visibleBorder);
}
BPoint delta = where - fLastMousePosition;
// NOTE: "delta" is later used to change fLastMousePosition.
// If for some reason no change should take effect, delta
// is to be set to (0, 0) so that fLastMousePosition is not
// adjusted. This way the relative mouse position to the
// item being changed (border during resizing, tab during
// sliding...) stays fixed when the mouse is moved so that
// changes are taking effect again.
// If the window was moved enough, it doesn't come to
// the front in FFM mode when the mouse is released.
if (fActivateOnMouseUp || fMinimizeCheckOnMouseUp) {
fMouseMoveDistance += delta.x * delta.x + delta.y * delta.y;
if (fMouseMoveDistance > 16.0f) {
fActivateOnMouseUp = false;
fMinimizeCheckOnMouseUp = false;
} else
delta = B_ORIGIN;
}
// moving
if (fIsDragging) {
if (!(fWindow->Flags() & B_NOT_MOVABLE)) {
BPoint oldLeftTop = fWindow->Frame().LeftTop();
_AlterDeltaForSnap(delta, now);
fDesktop->MoveWindowBy(fWindow, delta.x, delta.y);
// constrain delta to true change in position
delta = fWindow->Frame().LeftTop() - oldLeftTop;
} else
delta = BPoint(0, 0);
}
// resizing
if (fIsResizing) {
if (!(fWindow->Flags() & B_NOT_RESIZABLE)) {
if (fWindow->Flags() & B_NOT_V_RESIZABLE)
delta.y = 0;
if (fWindow->Flags() & B_NOT_H_RESIZABLE)
delta.x = 0;
BPoint oldRightBottom = fWindow->Frame().RightBottom();
fDesktop->ResizeWindowBy(fWindow, delta.x, delta.y);
// constrain delta to true change in size
delta = fWindow->Frame().RightBottom() - oldRightBottom;
} else
delta = BPoint(0, 0);
}
// sliding tab
if (fIsSlidingTab) {
float loc = fWindow->TabLocation();
// TODO: change to [0:1]
loc += delta.x;
if (fDesktop->SetWindowTabLocation(fWindow, loc))
delta.y = 0;
else
delta = BPoint(0, 0);
}
// NOTE: fLastMousePosition is currently only
// used for window moving/resizing/sliding the tab
fLastMousePosition += delta;
// change focus in FFM mode
DesktopSettings desktopSettings(fDesktop);
if (desktopSettings.FocusFollowsMouse()
&& !fWindow->IsFocus() && !(fWindow->Flags() & B_AVOID_FOCUS)) {
// If the mouse move is a fake one, we set the focus to NULL, which
// will cause the window that had focus last to retrieve it again - this
// makes FFM much nicer to use with the keyboard.
fDesktop->SetFocusWindow(isFake ? NULL : fWindow);
}
if (fState != NULL)
fState->MouseMoved(message, where, isFake);
}
@@ -542,70 +761,13 @@ DefaultWindowBehaviour::_RegionFor(const BMessage* message) const
void
DefaultWindowBehaviour::_AlterDeltaForSnap(BPoint& delta, bigtime_t now)
DefaultWindowBehaviour::_NextState(State* state)
{
// Alter the delta (which is a proposed offset used while dragging a
// window) so that the frame of the window 'snaps' to the edges of the
// screen.
State* oldState = fState;
fState = state;
const bigtime_t kSnappingDuration = 1500000LL;
const bigtime_t kSnappingPause = 3000000LL;
const float kSnapDistance = 8.0f;
if (fState != NULL)
fState->EnterState(oldState);
if (now - fLastSnapTime > kSnappingDuration
&& now - fLastSnapTime < kSnappingPause) {
// Maintain a pause between snapping.
return;
}
BRect frame = fWindow->Frame();
BPoint offsetWithinFrame;
// TODO: Perhaps obtain the usable area (not covered by the Deskbar)?
BRect screenFrame = fWindow->Screen()->Frame();
Decorator* decorator = fWindow->Decorator();
if (decorator) {
frame = decorator->GetFootprint().Frame();
offsetWithinFrame.x = fWindow->Frame().left - frame.left;
offsetWithinFrame.y = fWindow->Frame().top - frame.top;
}
frame.OffsetBy(delta);
float leftDist = fabs(frame.left - screenFrame.left);
float topDist = fabs(frame.top - screenFrame.top);
float rightDist = fabs(frame.right - screenFrame.right);
float bottomDist = fabs(frame.bottom - screenFrame.bottom);
bool snapped = false;
if (leftDist < kSnapDistance || rightDist < kSnapDistance) {
snapped = true;
if (leftDist < rightDist) {
frame.right -= frame.left;
frame.left = 0.0f;
} else {
frame.left -= frame.right - screenFrame.right;
frame.right = screenFrame.right;
}
}
if (topDist < kSnapDistance || bottomDist < kSnapDistance) {
snapped = true;
if (topDist < bottomDist) {
frame.bottom -= frame.top;
frame.top = 0.0f;
} else {
frame.top -= frame.bottom - screenFrame.bottom;
frame.bottom = screenFrame.bottom;
}
}
if (snapped && now - fLastSnapTime > kSnappingPause)
fLastSnapTime = now;
frame.top += offsetWithinFrame.y;
frame.left += offsetWithinFrame.x;
delta.y = frame.top - fWindow->Frame().top;
delta.x = frame.left - fWindow->Frame().left;
delete oldState;
}
+18 -14
View File
@@ -48,27 +48,31 @@ private:
REGION_RESIZE_CORNER
};
struct State;
struct MouseTrackingState;
struct DragState;
struct ResizeState;
struct SlideTabState;
struct DecoratorButtonState;
// to keep gcc 2 happy
friend struct State;
friend struct MouseTrackingState;
friend struct DragState;
friend struct ResizeState;
friend struct SlideTabState;
friend struct DecoratorButtonState;
private:
bool _IsWindowModifier(int32 modifiers) const;
Region _RegionFor(const BMessage* message) const;
void _AlterDeltaForSnap(BPoint& delta,
bigtime_t now);
void _NextState(State* state);
protected:
Window* fWindow;
Desktop* fDesktop;
bool fIsClosing : 1;
bool fIsMinimizing : 1;
bool fIsZooming : 1;
bool fIsSlidingTab : 1;
bool fActivateOnMouseUp : 1;
bool fMinimizeCheckOnMouseUp : 1;
BPoint fLastMousePosition;
float fMouseMoveDistance;
bigtime_t fLastMoveTime;
bigtime_t fLastSnapTime;
State* fState;
int32 fLastModifiers;
int32 fResetClickCount;
};