Author: bonefish Date: 2010-11-24 17:17:32 +0100 (Wed, 24 Nov 2010) New Revision: 39608 Changeset: http://dev.haiku-os.org/changeset/39608 Modified: haiku/trunk/src/servers/app/DefaultWindowBehaviour.cpp haiku/trunk/src/servers/app/DefaultWindowBehaviour.h Log: * 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. Modified: haiku/trunk/src/servers/app/DefaultWindowBehaviour.cpp =================================================================== --- haiku/trunk/src/servers/app/DefaultWindowBehaviour.cpp 2010-11-24 14:50:07 UTC (rev 39607) +++ haiku/trunk/src/servers/app/DefaultWindowBehaviour.cpp 2010-11-24 16:17:32 UTC (rev 39608) @@ -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 @@ 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 @@ 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 @@ 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 @@ 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; } Modified: haiku/trunk/src/servers/app/DefaultWindowBehaviour.h =================================================================== --- haiku/trunk/src/servers/app/DefaultWindowBehaviour.h 2010-11-24 14:50:07 UTC (rev 39607) +++ haiku/trunk/src/servers/app/DefaultWindowBehaviour.h 2010-11-24 16:17:32 UTC (rev 39608) @@ -48,27 +48,31 @@ 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; };