From 177ecc46b6e7dc8b210dc09b42fc0891b91249aa Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Thu, 15 Jul 2010 04:49:38 +0000 Subject: [PATCH] Refactoring of the Window class. Move window behaviour into separate WindowBehaviour class. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37524 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/servers/app/DefaultWindowBehaviour.cpp | 505 +++++++++++++++++++++ src/servers/app/DefaultWindowBehaviour.h | 54 +++ src/servers/app/Jamfile | 2 + src/servers/app/Window.cpp | 491 ++------------------ src/servers/app/Window.h | 34 +- src/servers/app/WindowBehaviour.cpp | 16 + src/servers/app/WindowBehaviour.h | 32 ++ 7 files changed, 644 insertions(+), 490 deletions(-) create mode 100644 src/servers/app/DefaultWindowBehaviour.cpp create mode 100644 src/servers/app/DefaultWindowBehaviour.h create mode 100644 src/servers/app/WindowBehaviour.cpp create mode 100644 src/servers/app/WindowBehaviour.h diff --git a/src/servers/app/DefaultWindowBehaviour.cpp b/src/servers/app/DefaultWindowBehaviour.cpp new file mode 100644 index 0000000000..08e5615e0f --- /dev/null +++ b/src/servers/app/DefaultWindowBehaviour.cpp @@ -0,0 +1,505 @@ +#include "DefaultWindowBehaviour.h" + +#include "Desktop.h" +#include "DrawingEngine.h" +#include "Window.h" + + +//#define DEBUG_WINDOW_CLICK + +#ifdef DEBUG_WINDOW_CLICK +# define STRACE_CLICK(x) printf x +#else +# define STRACE_CLICK(x) ; +#endif + + +DefaultWindowBehaviour::DefaultWindowBehaviour(Window* window, + Decorator* decorator) + : + fWindow(window), + fDecorator(decorator), + + fIsClosing(false), + fIsMinimizing(false), + fIsZooming(false), + fIsSlidingTab(false), + fActivateOnMouseUp(false), + + fLastMousePosition(0.0f, 0.0f), + fMouseMoveDistance(0.0f), + fLastMoveTime(0), + fLastSnapTime(0) +{ + fDesktop = fWindow->Desktop(); +} + + +DefaultWindowBehaviour::~DefaultWindowBehaviour() +{ + +} + + +static const bigtime_t kWindowActivationTimeout = 500000LL; + + +bool +DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where) +{ + int32 modifiers = _ExtractModifiers(message); + bool inBorderRegion = fWindow->BorderRegion().Contains(where); + bool windowModifier = + (fWindow->Flags() & B_NO_SERVER_SIDE_WINDOW_MODIFIERS) == 0 + && (modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY + | B_SHIFT_KEY)) == (B_COMMAND_KEY | B_CONTROL_KEY); + click_type action = CLICK_NONE; + + if (windowModifier || inBorderRegion) { + // clicking Window visible area + + int32 buttons = _ExtractButtons(message); + + if (inBorderRegion) + action = _ActionFor(message, buttons, modifiers); + else { + if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) + action = CLICK_MOVE_TO_BACK; + else if ((fWindow->Flags() & B_NOT_MOVABLE) == 0 + && fDecorator != NULL) + action = CLICK_DRAG; + else { + // pass click on to the application + windowModifier = false; + } + } + } + + DesktopSettings desktopSettings(fDesktop); + if (windowModifier || inBorderRegion) { + if (!desktopSettings.AcceptFirstClick()) { + // Ignore clicks on decorator buttons if the + // non-floating window doesn't have focus + if (!fWindow->IsFocus() && !fWindow->IsFloating() + && action != CLICK_MOVE_TO_BACK + && action != CLICK_RESIZE && action != CLICK_SLIDE_TAB) + action = CLICK_DRAG; + } + + // set decorator internals + 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")); + } + break; + + case CLICK_DRAG: + fIsDragging = true; + fLastMousePosition = where; + STRACE_CLICK(("===> CLICK_DRAG\n")); + break; + + case CLICK_RESIZE: + fIsResizing = true; + fLastMousePosition = where; + STRACE_CLICK(("===> CLICK_RESIZE\n")); + break; + + case CLICK_SLIDE_TAB: + fIsSlidingTab = true; + fLastMousePosition = where; + STRACE_CLICK(("===> CLICK_SLIDE_TAB\n")); + break; + + default: + break; + } + + if (fDecorator != NULL) { + // redraw decorator + BRegion* visibleBorder = fWindow->RegionPool()->GetRegion(); + fWindow->GetBorderRegion(visibleBorder); + visibleBorder->IntersectWith(&fWindow->VisibleRegion()); + + DrawingEngine* engine = fDecorator->GetDrawingEngine(); + engine->LockParallelAccess(); + engine->ConstrainClippingRegion(visibleBorder); + + if (fIsZooming) + fDecorator->SetZoom(true); + else if (fIsClosing) + fDecorator->SetClose(true); + else if (fIsMinimizing) + fDecorator->SetMinimize(true); + + engine->UnlockParallelAccess(); + + fWindow->RegionPool()->Recycle(visibleBorder); + } + + if (action == CLICK_MOVE_TO_BACK) { + if (desktopSettings.MouseMode() == B_CLICK_TO_FOCUS_MOUSE) { + bool covered = true; + BRegion fullRegion; + fWindow->GetFullRegion(&fullRegion); + if (fullRegion == fWindow->VisibleRegion()) { + // window is overlapped. + covered = false; + } + if (fWindow != fDesktop->FrontWindow() && covered) + fDesktop->ActivateWindow(fWindow); + else + fDesktop->SendWindowBehind(fWindow); + } else + fDesktop->SendWindowBehind(fWindow); + } else { + 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 (desktopSettings.MouseMode() == B_FOCUS_FOLLOWS_MOUSE + && (action == CLICK_DRAG || action == CLICK_RESIZE)) { + fActivateOnMouseUp = true; + fMouseMoveDistance = 0.0f; + fLastMoveTime = system_time(); + } + } + } + + return true; + } + return false; +} + + +void +DefaultWindowBehaviour::MouseUp(BMessage* message, BPoint where) +{ + bool invalidate = false; + if (fDecorator) { + click_type action = _ActionFor(message); + + // redraw decorator + BRegion* visibleBorder = fWindow->RegionPool()->GetRegion(); + fWindow->GetBorderRegion(visibleBorder); + visibleBorder->IntersectWith(&fWindow->VisibleRegion()); + + DrawingEngine* engine = fDecorator->GetDrawingEngine(); + engine->LockParallelAccess(); + engine->ConstrainClippingRegion(visibleBorder); + + if (fIsZooming) { + fIsZooming = false; + fDecorator->SetZoom(false); + if (action == CLICK_ZOOM) { + invalidate = true; + fWindow->ServerWindow()->NotifyZoom(); + } + } + if (fIsClosing) { + fIsClosing = false; + fDecorator->SetClose(false); + if (action == CLICK_CLOSE) { + invalidate = true; + fWindow->ServerWindow()->NotifyQuitRequested(); + } + } + if (fIsMinimizing) { + fIsMinimizing = false; + fDecorator->SetMinimize(false); + if (action == CLICK_MINIMIZE) { + invalidate = true; + fWindow->ServerWindow()->NotifyMinimize(true); + } + } + + engine->UnlockParallelAccess(); + + fWindow->RegionPool()->Recycle(visibleBorder); + + int32 buttons; + if (message->FindInt32("buttons", &buttons) != B_OK) + buttons = 0; + + // if the primary mouse button is released, stop + // dragging/resizing/sliding + if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0) { + fIsDragging = false; + fIsResizing = false; + fIsSlidingTab = false; + } + } + + // 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); + } +} + + +void +DefaultWindowBehaviour::MouseMoved(BMessage *message, BPoint where, bool isFake) +{ + #if 0 + if (fDecorator != NULL && fWindow->TopView() != NULL) { + DrawingEngine* engine = fDecorator->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) { + if (now - fLastMoveTime >= kWindowActivationTimeout) { + // This click is too long already for window activation. + fActivateOnMouseUp = false; + } + } else + fLastMoveTime = now; + } + + if (fDecorator) { + BRegion* visibleBorder = fWindow->RegionPool()->GetRegion(); + fWindow->GetBorderRegion(visibleBorder); + visibleBorder->IntersectWith(&fWindow->VisibleRegion()); + + DrawingEngine* engine = fDecorator->GetDrawingEngine(); + engine->LockParallelAccess(); + engine->ConstrainClippingRegion(visibleBorder); + + if (fIsZooming) { + fDecorator->SetZoom(_ActionFor(message) == CLICK_ZOOM); + } else if (fIsClosing) { + fDecorator->SetClose(_ActionFor(message) == CLICK_CLOSE); + } else if (fIsMinimizing) { + fDecorator->SetMinimize(_ActionFor(message) == CLICK_MINIMIZE); + } + + 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) { + fMouseMoveDistance += delta.x * delta.x + delta.y * delta.y; + if (fMouseMoveDistance > 16.0f) + fActivateOnMouseUp = 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); + } +} + + +int32 +DefaultWindowBehaviour::_ExtractButtons(const BMessage* message) const +{ + int32 buttons; + if (message->FindInt32("buttons", &buttons) != B_OK) + buttons = 0; + return buttons; +} + + +int32 +DefaultWindowBehaviour::_ExtractModifiers(const BMessage* message) const +{ + int32 modifiers; + if (message->FindInt32("modifiers", &modifiers) != B_OK) + modifiers = 0; + return modifiers; +} + + +click_type +DefaultWindowBehaviour::_ActionFor(const BMessage* message) const +{ + if (fDecorator == NULL) + return CLICK_NONE; + + int32 buttons = _ExtractButtons(message); + int32 modifiers = _ExtractModifiers(message); + return _ActionFor(message, buttons, modifiers); +} + + +click_type +DefaultWindowBehaviour::_ActionFor(const BMessage* message, int32 buttons, + int32 modifiers) const +{ + if (fDecorator == NULL) + return CLICK_NONE; + + BPoint where; + if (message->FindPoint("where", &where) != B_OK) + return CLICK_NONE; + + return fDecorator->Clicked(where, buttons, modifiers); +} + + +void +DefaultWindowBehaviour::_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(); + + if (fDecorator) { + BRegion reg; + fDecorator->GetFootprint(®); + frame = reg.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; +} diff --git a/src/servers/app/DefaultWindowBehaviour.h b/src/servers/app/DefaultWindowBehaviour.h new file mode 100644 index 0000000000..7e8a007c9d --- /dev/null +++ b/src/servers/app/DefaultWindowBehaviour.h @@ -0,0 +1,54 @@ +#ifndef DEFAULT_WINDOW_BEHAVIOUR_H +#define DEFAULT_WINDOW_BEHAVIOUR_H + + +#include "WindowBehaviour.h" + +#include "Decorator.h" + + +class Desktop; +class Window; + + +class DefaultWindowBehaviour : public WindowBehaviour +{ + public: + DefaultWindowBehaviour(Window* window, + Decorator* decorator); + virtual ~DefaultWindowBehaviour(); + + virtual bool MouseDown(BMessage* message, BPoint where); + virtual void MouseUp(BMessage* message, BPoint where); + virtual void MouseMoved(BMessage *message, BPoint where, + bool isFake); + + protected: + Window* fWindow; + Decorator* fDecorator; + Desktop* fDesktop; + + bool fIsClosing : 1; + bool fIsMinimizing : 1; + bool fIsZooming : 1; + bool fIsSlidingTab : 1; + bool fActivateOnMouseUp : 1; + + BPoint fLastMousePosition; + float fMouseMoveDistance; + bigtime_t fLastMoveTime; + bigtime_t fLastSnapTime; + + private: + int32 _ExtractButtons(const BMessage* message) const; + int32 _ExtractModifiers(const BMessage* message) const; + click_type _ActionFor(const BMessage* message) const; + click_type _ActionFor(const BMessage* message, int32 buttons, + int32 modifiers) const; + + void _AlterDeltaForSnap(BPoint& delta, + bigtime_t now); +}; + + +#endif diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 00e0d60224..70d174946a 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -21,6 +21,7 @@ Server app_server : DecorManager.cpp Decorator.cpp DefaultDecorator.cpp + DefaultWindowBehaviour.cpp Desktop.cpp DesktopSettings.cpp DirectWindowInfo.cpp @@ -57,6 +58,7 @@ Server app_server : View.cpp VirtualScreen.cpp Window.cpp + WindowBehaviour.cpp WindowList.cpp Workspace.cpp WorkspacesView.cpp diff --git a/src/servers/app/Window.cpp b/src/servers/app/Window.cpp index 89fea734bb..6c7df87a65 100644 --- a/src/servers/app/Window.cpp +++ b/src/servers/app/Window.cpp @@ -16,6 +16,7 @@ #include "Decorator.h" #include "DecorManager.h" #include "Desktop.h" +#include "DefaultWindowBehaviour.h" #include "DrawingEngine.h" #include "HWInterface.h" #include "MessagePrivate.h" @@ -39,7 +40,6 @@ // Toggle debug output //#define DEBUG_WINDOW -//#define DEBUG_WINDOW_CLICK #ifdef DEBUG_WINDOW # define STRACE(x) printf x @@ -47,12 +47,6 @@ # define STRACE(x) ; #endif -#ifdef DEBUG_WINDOW_CLICK -# define STRACE_CLICK(x) printf x -#else -# define STRACE_CLICK(x) ; -#endif - // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker) using std::nothrow; @@ -96,25 +90,13 @@ Window::Window(const BRect& frame, const char *name, fRegionPool(), - fIsClosing(false), - fIsMinimizing(false), - fIsZooming(false), - fIsResizing(false), - fIsSlidingTab(false), - fIsDragging(false), - fActivateOnMouseUp(false), - + fWindowBehaviour(NULL), fDecorator(NULL), fTopView(NULL), fWindow(window), fDrawingEngine(drawingEngine), fDesktop(window->Desktop()), - fLastMousePosition(0.0f, 0.0f), - fMouseMoveDistance(0.0f), - fLastMoveTime(0), - fLastSnapTime(0), - fCurrentUpdateSession(&fUpdateSessions[0]), fPendingUpdateSession(&fUpdateSessions[1]), fUpdateRequested(false), @@ -154,6 +136,8 @@ Window::Window(const BRect& frame, const char *name, &fMaxWidth, &fMaxHeight); } } + fWindowBehaviour = new (std::nothrow)DefaultWindowBehaviour(this, + fDecorator); // do we need to change our size to let the decorator fit? // _ResizeBy() will adapt the frame for validity before resizing @@ -187,6 +171,7 @@ Window::~Window() delete fTopView; } + delete fWindowBehaviour; delete fDecorator; delete fDrawingEngine; @@ -196,7 +181,7 @@ Window::~Window() status_t Window::InitCheck() const { - if (!fDrawingEngine) + if (!fDrawingEngine || !fWindowBehaviour) return B_NO_MEMORY; // TODO: anything else? return B_OK; @@ -775,9 +760,6 @@ Window::EnableUpdateRequests() // #pragma mark - -static const bigtime_t kWindowActivationTimeout = 500000LL; - - void Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken) { @@ -787,136 +769,8 @@ Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken) if (!fBorderRegionValid) GetBorderRegion(&fBorderRegion); - int32 modifiers = _ExtractModifiers(message); - bool inBorderRegion = fBorderRegion.Contains(where); - bool windowModifier = (fFlags & B_NO_SERVER_SIDE_WINDOW_MODIFIERS) == 0 - && (modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY - | B_SHIFT_KEY)) == (B_COMMAND_KEY | B_CONTROL_KEY); - click_type action = CLICK_NONE; - - if (windowModifier || inBorderRegion) { - // clicking Window visible area - - int32 buttons = _ExtractButtons(message); - - if (inBorderRegion) - action = _ActionFor(message, buttons, modifiers); - else { - if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) - action = CLICK_MOVE_TO_BACK; - else if ((fFlags & B_NOT_MOVABLE) == 0 && fDecorator != NULL) - action = CLICK_DRAG; - else { - // pass click on to the application - windowModifier = false; - } - } - } - - if (windowModifier || inBorderRegion) { - if (!desktopSettings.AcceptFirstClick()) { - // Ignore clicks on decorator buttons if the - // non-floating window doesn't have focus - if (!IsFocus() && !IsFloating() && action != CLICK_MOVE_TO_BACK - && action != CLICK_RESIZE && action != CLICK_SLIDE_TAB) - action = CLICK_DRAG; - } - - // set decorator internals - 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 ((Flags() & B_NOT_MINIMIZABLE) == 0) { - fIsMinimizing = true; - STRACE_CLICK(("===> CLICK_MINIMIZE\n")); - } - break; - - case CLICK_DRAG: - fIsDragging = true; - fLastMousePosition = where; - STRACE_CLICK(("===> CLICK_DRAG\n")); - break; - - case CLICK_RESIZE: - fIsResizing = true; - fLastMousePosition = where; - STRACE_CLICK(("===> CLICK_RESIZE\n")); - break; - - case CLICK_SLIDE_TAB: - fIsSlidingTab = true; - fLastMousePosition = where; - STRACE_CLICK(("===> CLICK_SLIDE_TAB\n")); - break; - - default: - break; - } - - if (fDecorator != NULL) { - // redraw decorator - BRegion* visibleBorder = fRegionPool.GetRegion(); - GetBorderRegion(visibleBorder); - visibleBorder->IntersectWith(&VisibleRegion()); - - DrawingEngine* engine = fDecorator->GetDrawingEngine(); - engine->LockParallelAccess(); - engine->ConstrainClippingRegion(visibleBorder); - - if (fIsZooming) - fDecorator->SetZoom(true); - else if (fIsClosing) - fDecorator->SetClose(true); - else if (fIsMinimizing) - fDecorator->SetMinimize(true); - - engine->UnlockParallelAccess(); - - fRegionPool.Recycle(visibleBorder); - } - - if (action == CLICK_MOVE_TO_BACK) { - if (desktopSettings.MouseMode() == B_CLICK_TO_FOCUS_MOUSE) { - bool covered = true; - BRegion fullRegion; - GetFullRegion(&fullRegion); - if (fullRegion == VisibleRegion()) { - // window is overlapped. - covered = false; - } - if (this != fDesktop->FrontWindow() && covered) - fDesktop->ActivateWindow(this); - else - fDesktop->SendWindowBehind(this); - } else - fDesktop->SendWindowBehind(this); - } else { - fDesktop->SetMouseEventWindow(this); - - // activate window if in click to activate mode, else only focus it - if (desktopSettings.MouseMode() == B_NORMAL_MOUSE) - fDesktop->ActivateWindow(this); - else { - fDesktop->SetFocusWindow(this); - if (desktopSettings.MouseMode() == B_FOCUS_FOLLOWS_MOUSE - && (action == CLICK_DRAG || action == CLICK_RESIZE)) { - fActivateOnMouseUp = true; - fMouseMoveDistance = 0.0f; - fLastMoveTime = system_time(); - } - } - } - } else { + bool eventEaten = fWindowBehaviour->MouseDown(message, where); + if (!eventEaten) { // click was inside the window contents if (View* view = ViewAt(where)) { if (HasModal()) @@ -957,71 +811,7 @@ Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken) void Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken) { - bool invalidate = false; - if (fDecorator) { - click_type action = _ActionFor(message); - - // redraw decorator - BRegion* visibleBorder = fRegionPool.GetRegion(); - GetBorderRegion(visibleBorder); - visibleBorder->IntersectWith(&VisibleRegion()); - - DrawingEngine* engine = fDecorator->GetDrawingEngine(); - engine->LockParallelAccess(); - engine->ConstrainClippingRegion(visibleBorder); - - if (fIsZooming) { - fIsZooming = false; - fDecorator->SetZoom(false); - if (action == CLICK_ZOOM) { - invalidate = true; - fWindow->NotifyZoom(); - } - } - if (fIsClosing) { - fIsClosing = false; - fDecorator->SetClose(false); - if (action == CLICK_CLOSE) { - invalidate = true; - fWindow->NotifyQuitRequested(); - } - } - if (fIsMinimizing) { - fIsMinimizing = false; - fDecorator->SetMinimize(false); - if (action == CLICK_MINIMIZE) { - invalidate = true; - fWindow->NotifyMinimize(true); - } - } - - engine->UnlockParallelAccess(); - - fRegionPool.Recycle(visibleBorder); - - int32 buttons; - if (message->FindInt32("buttons", &buttons) != B_OK) - buttons = 0; - - // if the primary mouse button is released, stop - // dragging/resizing/sliding - if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0) { - fIsDragging = false; - fIsResizing = false; - fIsSlidingTab = false; - } - } - - // 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(this); - } + fWindowBehaviour->MouseUp(message, where); if (View* view = ViewAt(where)) { if (HasModal()) @@ -1037,17 +827,6 @@ void Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, bool isLatestMouseMoved, bool isFake) { -#if 0 - if (fDecorator != NULL && fTopView != NULL) { - DrawingEngine* engine = fDecorator->GetDrawingEngine(); - engine->LockParallelAccess(); - engine->ConstrainClippingRegion(&VisibleRegion()); - - fTopView->MarkAt(engine, where); - engine->UnlockParallelAccess(); - } -#endif - View* view = ViewAt(where); if (view != NULL) *_viewToken = view->Token(); @@ -1056,119 +835,7 @@ Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, if (!isLatestMouseMoved) return; - // 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) { - if (now - fLastMoveTime >= kWindowActivationTimeout) { - // This click is too long already for window activation. - fActivateOnMouseUp = false; - } - } else - fLastMoveTime = now; - } - - if (fDecorator) { - BRegion* visibleBorder = fRegionPool.GetRegion(); - GetBorderRegion(visibleBorder); - visibleBorder->IntersectWith(&VisibleRegion()); - - DrawingEngine* engine = fDecorator->GetDrawingEngine(); - engine->LockParallelAccess(); - engine->ConstrainClippingRegion(visibleBorder); - - if (fIsZooming) { - fDecorator->SetZoom(_ActionFor(message) == CLICK_ZOOM); - } else if (fIsClosing) { - fDecorator->SetClose(_ActionFor(message) == CLICK_CLOSE); - } else if (fIsMinimizing) { - fDecorator->SetMinimize(_ActionFor(message) == CLICK_MINIMIZE); - } - - engine->UnlockParallelAccess(); - fRegionPool.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) { - fMouseMoveDistance += delta.x * delta.x + delta.y * delta.y; - if (fMouseMoveDistance > 16.0f) - fActivateOnMouseUp = false; - else - delta = B_ORIGIN; - } - - // moving - if (fIsDragging) { - if (!(Flags() & B_NOT_MOVABLE)) { - BPoint oldLeftTop = fFrame.LeftTop(); - - _AlterDeltaForSnap(delta, now); - fDesktop->MoveWindowBy(this, delta.x, delta.y); - - // constrain delta to true change in position - delta = fFrame.LeftTop() - oldLeftTop; - } else - delta = BPoint(0, 0); - } - // resizing - if (fIsResizing) { - if (!(Flags() & B_NOT_RESIZABLE)) { - if (Flags() & B_NOT_V_RESIZABLE) - delta.y = 0; - if (Flags() & B_NOT_H_RESIZABLE) - delta.x = 0; - - BPoint oldRightBottom = fFrame.RightBottom(); - - fDesktop->ResizeWindowBy(this, delta.x, delta.y); - - // constrain delta to true change in size - delta = fFrame.RightBottom() - oldRightBottom; - } else - delta = BPoint(0, 0); - } - // sliding tab - if (fIsSlidingTab) { - float loc = TabLocation(); - // TODO: change to [0:1] - loc += delta.x; - if (fDesktop->SetWindowTabLocation(this, 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() - && !IsFocus() && !(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 : this); - } + fWindowBehaviour->MouseMoved(message, where, isFake); // mouse cursor @@ -1182,77 +849,6 @@ Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, } -void -Window::_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 = fFrame; - BPoint offsetWithinFrame; - // TODO: Perhaps obtain the usable area (not covered by the Deskbar)? - BRect screenFrame = Screen()->Frame(); - - if (fDecorator) { - BRegion reg; - fDecorator->GetFootprint(®); - frame = reg.Frame(); - offsetWithinFrame.x = fFrame.left - frame.left; - offsetWithinFrame.y = fFrame.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 - fFrame.top; - delta.x = frame.left - fFrame.left; -} - - // #pragma mark - @@ -1375,6 +971,24 @@ Window::IsVisible() const } +bool +Window::IsDragging() const +{ + if (!fWindowBehaviour) + return false; + return fWindowBehaviour->IsDragging(); +} + + +bool +Window::IsResizing() const +{ + if (!fWindowBehaviour) + return false; + return fWindowBehaviour->IsResizing(); +} + + void Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight, int32 maxHeight) @@ -2226,53 +1840,6 @@ Window::_UpdateContentRegion() } -int32 -Window::_ExtractButtons(const BMessage* message) const -{ - int32 buttons; - if (message->FindInt32("buttons", &buttons) != B_OK) - buttons = 0; - return buttons; -} - - -int32 -Window::_ExtractModifiers(const BMessage* message) const -{ - int32 modifiers; - if (message->FindInt32("modifiers", &modifiers) != B_OK) - modifiers = 0; - return modifiers; -} - - -click_type -Window::_ActionFor(const BMessage* message) const -{ - if (fDecorator == NULL) - return CLICK_NONE; - - int32 buttons = _ExtractButtons(message); - int32 modifiers = _ExtractModifiers(message); - return _ActionFor(message, buttons, modifiers); -} - - -click_type -Window::_ActionFor(const BMessage* message, int32 buttons, - int32 modifiers) const -{ - if (fDecorator == NULL) - return CLICK_NONE; - - BPoint where; - if (message->FindPoint("where", &where) != B_OK) - return CLICK_NONE; - - return fDecorator->Clicked(where, buttons, modifiers); -} - - void Window::_ObeySizeLimits() { @@ -2370,5 +1937,3 @@ Window::UpdateSession::AddCause(uint8 cause) { fCause |= cause; } - - diff --git a/src/servers/app/Window.h b/src/servers/app/Window.h index de82098229..242b7197ad 100644 --- a/src/servers/app/Window.h +++ b/src/servers/app/Window.h @@ -33,6 +33,7 @@ class Desktop; class DrawingEngine; class EventDispatcher; class Screen; +class WindowBehaviour; class WorkspacesView; // TODO: move this into a proper place @@ -169,10 +170,12 @@ public: void SetCurrentWorkspace(int32 index) { fCurrentWorkspace = index; } + int32 CurrentWorkspace() const + { return fCurrentWorkspace; } bool IsVisible() const; - bool IsDragging() const { return fIsDragging; } - bool IsResizing() const { return fIsResizing; } + bool IsDragging() const; + bool IsResizing() const; void SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight, int32 maxHeight); @@ -244,6 +247,7 @@ public: static uint32 ValidWindowFlags(); static uint32 ValidWindowFlags(window_feel feel); + BRegion& BorderRegion() { return fBorderRegion; } protected: void _ShiftPartOfRegion(BRegion* region, BRegion* regionToShift, int32 xOffset, @@ -260,20 +264,9 @@ protected: void _UpdateContentRegion(); - int32 _ExtractButtons( - const BMessage* message) const; - int32 _ExtractModifiers( - const BMessage* message) const; - click_type _ActionFor(const BMessage* message) const; - click_type _ActionFor(const BMessage* message, - int32 buttons, int32 modifiers) const; - void _ObeySizeLimits(); void _PropagatePosition(); - void _AlterDeltaForSnap(BPoint& delta, - bigtime_t now); - BString fTitle; // TODO: no fp rects anywhere BRect fFrame; @@ -309,26 +302,13 @@ protected: BObjectList fSubsets; -// TODO: remove those some day (let the decorator handle that stuff) - bool fIsClosing : 1; - bool fIsMinimizing : 1; - bool fIsZooming : 1; - bool fIsResizing : 1; - bool fIsSlidingTab : 1; - bool fIsDragging : 1; - bool fActivateOnMouseUp : 1; - + WindowBehaviour* fWindowBehaviour; ::Decorator* fDecorator; View* fTopView; ::ServerWindow* fWindow; DrawingEngine* fDrawingEngine; ::Desktop* fDesktop; - BPoint fLastMousePosition; - float fMouseMoveDistance; - bigtime_t fLastMoveTime; - bigtime_t fLastSnapTime; - // The synchronization, which client drawing commands // belong to the redraw of which dirty region is handled // through an UpdateSession. When the client has diff --git a/src/servers/app/WindowBehaviour.cpp b/src/servers/app/WindowBehaviour.cpp new file mode 100644 index 0000000000..9bfa171f8e --- /dev/null +++ b/src/servers/app/WindowBehaviour.cpp @@ -0,0 +1,16 @@ +#include "WindowBehaviour.h" + + +WindowBehaviour::WindowBehaviour() + : + fIsResizing(false), + fIsDragging(false) +{ + +} + + +WindowBehaviour::~WindowBehaviour() +{ + +} diff --git a/src/servers/app/WindowBehaviour.h b/src/servers/app/WindowBehaviour.h new file mode 100644 index 0000000000..6bc2f6902d --- /dev/null +++ b/src/servers/app/WindowBehaviour.h @@ -0,0 +1,32 @@ +#ifndef WINDOW_BEHAVIOUR_H +#define WINDOW_BEHAVIOUR_H + + +#include + + +class BMessage; + + +class WindowBehaviour +{ + 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 void MouseUp(BMessage* message, BPoint where) = 0; + virtual void MouseMoved(BMessage *message, BPoint where, + bool isFake) = 0; + + bool IsDragging() const { return fIsDragging; } + bool IsResizing() const { return fIsResizing; } + + protected: + bool fIsResizing : 1; + bool fIsDragging : 1; +}; + + +#endif