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