Ken Thomases : winemac: Restore a maximized window if a user tries to move it by dragging its title bar.

Alexandre Julliard julliard at wine.codeweavers.com
Tue Mar 24 10:00:43 CDT 2015


Module: wine
Branch: master
Commit: 792b47ad3b89d51f17ef311ccaa62fc2016af793
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=792b47ad3b89d51f17ef311ccaa62fc2016af793

Author: Ken Thomases <ken at codeweavers.com>
Date:   Mon Mar 23 18:58:11 2015 -0500

winemac: Restore a maximized window if a user tries to move it by dragging its title bar.

OS X doesn't have the same concept of maximized windows as Windows does.
There's no mode that prevents a normally-movable window from being moved.  If
a window is "zoomed", it mostly fills the screen but the user can still move
or resize it, at which point it ceases to be in the zoomed state.  So, users
are confused and frustrated when they can't move a window that's maximized.

To get similar behavior while still respecting Win32 semantics, we detect when
the user tries to move a maximized window.  When they start, a request is
submitted to the app to restore the window.  Unless and until the window is
restored, we don't actually allow the window to move.

The user expects to move the window from its current (maximized) position.  It
should not jump to its normal position upon being restored.  So, we set the
window's normal position to its current position before restoring it.

---

 dlls/winemac.drv/cocoa_app.h    |   2 +
 dlls/winemac.drv/cocoa_app.m    |   9 +++
 dlls/winemac.drv/cocoa_window.h |   4 ++
 dlls/winemac.drv/cocoa_window.m | 129 +++++++++++++++++++++++++++++++++++++---
 4 files changed, 135 insertions(+), 9 deletions(-)

diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h
index fc6c1ec..6ca3fcb 100644
--- a/dlls/winemac.drv/cocoa_app.h
+++ b/dlls/winemac.drv/cocoa_app.h
@@ -119,6 +119,8 @@ enum {
     - (void) keyboardSelectionDidChange;
     - (void) noteKey:(uint16_t)keyCode pressed:(BOOL)pressed;
 
+    - (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged;
+
     - (void) flipRect:(NSRect*)rect;
 
     - (WineWindow*) frontWineWindow;
diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m
index 5afb7f8..82925c8 100644
--- a/dlls/winemac.drv/cocoa_app.m
+++ b/dlls/winemac.drv/cocoa_app.m
@@ -1436,6 +1436,15 @@ int macdrv_err_on;
             pressedKeyCodes[index] &= ~mask;
     }
 
+    - (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged
+    {
+        if (dragged)
+            [windowsBeingDragged addObject:window];
+        else
+            [windowsBeingDragged removeObject:window];
+        [self updateCursorClippingState];
+    }
+
     - (void) handleMouseMove:(NSEvent*)anEvent
     {
         WineWindow* targetWindow;
diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h
index 574ff75..f5ff3b7 100644
--- a/dlls/winemac.drv/cocoa_window.h
+++ b/dlls/winemac.drv/cocoa_window.h
@@ -69,6 +69,10 @@
     NSRect nonFullscreenFrame;
     NSTimeInterval enteredFullScreenTime;
 
+    int draggingPhase;
+    NSPoint dragStartPosition;
+    NSPoint dragWindowStartPosition;
+
     BOOL ignore_windowDeminiaturize;
     BOOL ignore_windowResize;
     BOOL fakingClose;
diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m
index 1bb99a9..f68a1c5 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -653,6 +653,16 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
     }
 
+    - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
+    {
+        if (allow_immovable_windows && (disabled || inMaximized))
+            return NO;
+        else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
+            return NO;
+        else
+            return YES;
+    }
+
     - (void) adjustFeaturesForState
     {
         NSUInteger style = [self styleMask];
@@ -682,14 +692,7 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
         }
 
         if (allow_immovable_windows || cursor_clipping_locks_windows)
-        {
-            if (allow_immovable_windows && (disabled || maximized))
-                [self setMovable:NO];
-            else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
-                [self setMovable:NO];
-            else
-                [self setMovable:YES];
-        }
+            [self setMovable:[self allowsMovingWithMaximized:maximized]];
     }
 
     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
@@ -1543,6 +1546,22 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
         [self adjustFeaturesForState];
     }
 
+    - (void) endWindowDragging
+    {
+        if (draggingPhase)
+        {
+            if (draggingPhase == 3)
+            {
+                macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
+                [queue postEvent:event];
+                macdrv_release_event(event);
+            }
+
+            draggingPhase = 0;
+            [[WineApplicationController sharedController] window:self isBeingDragged:NO];
+        }
+    }
+
 
     /*
      * ---------- NSWindow method overrides ----------
@@ -1603,14 +1622,104 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
 
     - (void) sendEvent:(NSEvent*)event
     {
+        NSEventType type = event.type;
+
         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
            interface control.  For example, Control-Tab switches focus among
            views.  We want to bypass that feature, so directly route key-down
            events to -keyDown:. */
-        if ([event type] == NSKeyDown)
+        if (type == NSKeyDown)
             [[self firstResponder] keyDown:event];
         else
+        {
+            if (!draggingPhase && maximized && ![self isMovable] &&
+                ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
+                type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
+            {
+                NSRect titleBar = self.frame;
+                NSRect contentRect = [self contentRectForFrameRect:titleBar];
+                titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
+                titleBar.origin.y = NSMaxY(contentRect);
+
+                dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
+
+                if (NSMouseInRect(dragStartPosition, titleBar, NO))
+                {
+                    static const NSWindowButton buttons[] = {
+                        NSWindowCloseButton,
+                        NSWindowMiniaturizeButton,
+                        NSWindowZoomButton,
+                        NSWindowFullScreenButton,
+                    };
+                    BOOL hitButton = NO;
+                    int i;
+
+                    for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
+                    {
+                        NSButton* button;
+
+                        if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
+                            continue;
+
+                        button = [self standardWindowButton:buttons[i]];
+                        if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
+                        {
+                            hitButton = YES;
+                            break;
+                        }
+                    }
+
+                    if (!hitButton)
+                    {
+                        draggingPhase = 1;
+                        dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
+                        [[WineApplicationController sharedController] window:self isBeingDragged:YES];
+                    }
+                }
+            }
+            else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
+            {
+                if ([self isMovable])
+                {
+                    NSPoint point = [self convertBaseToScreen:event.locationInWindow];
+                    NSPoint newTopLeft = dragWindowStartPosition;
+
+                    newTopLeft.x += point.x - dragStartPosition.x;
+                    newTopLeft.y += point.y - dragStartPosition.y;
+
+                    if (draggingPhase == 2)
+                    {
+                        macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
+                        [queue postEvent:event];
+                        macdrv_release_event(event);
+
+                        draggingPhase = 3;
+                    }
+
+                    [self setFrameTopLeftPoint:newTopLeft];
+                }
+                else if (draggingPhase == 1 && type == NSLeftMouseDragged)
+                {
+                    macdrv_event* event;
+                    NSRect frame = [self contentRectForFrameRect:self.frame];
+
+                    [[WineApplicationController sharedController] flipRect:&frame];
+
+                    event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
+                    event->window_restore_requested.keep_frame = TRUE;
+                    event->window_restore_requested.frame = NSRectToCGRect(frame);
+                    [queue postEvent:event];
+                    macdrv_release_event(event);
+
+                    draggingPhase = 2;
+                }
+
+                if (type == NSLeftMouseUp)
+                    [self endWindowDragging];
+            }
+
             [super sendEvent:event];
+        }
     }
 
     - (void) miniaturize:(id)sender
@@ -2011,6 +2120,8 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
 
     - (void) windowWillStartLiveResize:(NSNotification *)notification
     {
+        [self endWindowDragging];
+
         if (maximized)
         {
             macdrv_event* event;




More information about the wine-cvs mailing list