Ken Thomases : winemac: Use a snapshot of an owned window when a zero-sized owner window is minimized.

Alexandre Julliard julliard at wine.codeweavers.com
Fri Oct 23 09:57:28 CDT 2015


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

Author: Ken Thomases <ken at codeweavers.com>
Date:   Fri Oct 23 02:48:34 2015 -0500

winemac: Use a snapshot of an owned window when a zero-sized owner window is minimized.

Some apps create a zero-sized window as their "main" window and then create
all of the other top-level windows as owned windows with that main window as
the owner.  The user interacts with these owned windows.  When the user
attempts to minimize one of these owned windows, the app instead minimizes the
zero-sized owner window.  When an owner window is minimized, all of its owned
windows are hidden.

The Mac driver faithfully carries out these window operations.  The only
visible windows are hidden and the zero-sized window is minimized.  This
results in an invisible animation of the window down to a slot in the Dock -
a slot which appears mostly empty.  The invisible window thumbnail is badged
with the app icon, but it still looks strange.

On Windows, the Alt-Tab switcher uses the image of the owned window to
represent the zero-sized owner.

This commit attempts to do something similar.  It takes over drawing of the
Dock icon for minimized, zero-sized window.  It grabs a snapshot of one of the
owned windows and draws the app badge onto it.  Since the owned windows are
hidden before the zero-sized owner is minimized and we can't take snapshots of
hidden windows, we use heuristics to guess when it may be useful to grab the
snapshot.  If the user minimizes an owned window from the Cocoa side, we grab
that window's snapshot.  If an owned window is being hidden and no snapshot has
been taken recently, we grab its snapshot on the theory that this may be the
beginning of hiding all of the owned windows before minimizing the owner.

Unfortunately, this doesn't address the invisible animations when minimizing
and unminimizing the zero-sized owner window.

Signed-off-by: Ken Thomases <ken at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/winemac.drv/cocoa_window.h |   2 +
 dlls/winemac.drv/cocoa_window.m | 114 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 116 insertions(+)

diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h
index f5ff3b7..9553b92 100644
--- a/dlls/winemac.drv/cocoa_window.h
+++ b/dlls/winemac.drv/cocoa_window.h
@@ -73,6 +73,8 @@
     NSPoint dragStartPosition;
     NSPoint dragWindowStartPosition;
 
+    NSTimeInterval lastDockIconSnapshot;
+
     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 35488b0..0dd6a6f 100644
--- a/dlls/winemac.drv/cocoa_window.m
+++ b/dlls/winemac.drv/cocoa_window.m
@@ -1265,6 +1265,10 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
         if ([self isMiniaturized])
             pendingMinimize = TRUE;
 
+        WineWindow* parent = (WineWindow*)self.parentWindow;
+        if ([parent isKindOfClass:[WineWindow class]])
+            [parent grabDockIconSnapshotFromWindow:self force:NO];
+
         [self becameIneligibleParentOrChild];
         if ([self isMiniaturized])
         {
@@ -1569,6 +1573,109 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
         }
     }
 
+    - (BOOL) isEmptyShaped
+    {
+        return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
+    }
+
+    - (BOOL) canProvideSnapshot
+    {
+        return (self.windowNumber > 0 && ![self isEmptyShaped]);
+    }
+
+    - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
+    {
+        if (![self isEmptyShaped])
+            return;
+
+        NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
+        if (!force && now < lastDockIconSnapshot + 1)
+            return;
+
+        if (window)
+        {
+            if (![window canProvideSnapshot])
+                return;
+        }
+        else
+        {
+            CGFloat bestArea;
+            for (WineWindow* childWindow in self.childWindows)
+            {
+                if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
+                    continue;
+
+                NSSize size = childWindow.frame.size;
+                CGFloat area = size.width * size.height;
+                if (!window || area > bestArea)
+                {
+                    window = childWindow;
+                    bestArea = area;
+                }
+            }
+
+            if (!window)
+                return;
+        }
+
+        const void* windowID = (const void*)(CGWindowID)window.windowNumber;
+        CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
+        CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
+        CFRelease(windowIDs);
+        if (!windowImage)
+            return;
+
+        NSImage* appImage = [NSApp applicationIconImage];
+        if (!appImage)
+            appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
+
+        NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
+        [dockIcon lockFocus];
+
+        CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
+
+        CGRect rect = CGRectMake(8, 8, 240, 240);
+        size_t width = CGImageGetWidth(windowImage);
+        size_t height = CGImageGetHeight(windowImage);
+        if (width > height)
+        {
+            rect.size.height *= height / (double)width;
+            rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
+        }
+        else if (width != height)
+        {
+            rect.size.width *= width / (double)height;
+            rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
+        }
+
+        CGContextDrawImage(cgcontext, rect, windowImage);
+        [appImage drawInRect:NSMakeRect(156, 4, 96, 96)];
+
+        [dockIcon unlockFocus];
+
+        CGImageRelease(windowImage);
+
+        NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
+        if (![imageView isKindOfClass:[NSImageView class]])
+        {
+            imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
+            imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
+            self.dockTile.contentView = imageView;
+        }
+        imageView.image = dockIcon;
+        [self.dockTile display];
+        lastDockIconSnapshot = now;
+    }
+
+    - (void) checkEmptyShaped
+    {
+        if (self.dockTile.contentView && ![self isEmptyShaped])
+        {
+            self.dockTile.contentView = nil;
+            lastDockIconSnapshot = 0;
+        }
+    }
+
 
     /*
      * ---------- NSWindow method overrides ----------
@@ -1766,6 +1873,10 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
         [queue postEvent:event];
         macdrv_release_event(event);
+
+        WineWindow* parent = (WineWindow*)self.parentWindow;
+        if ([parent isKindOfClass:[WineWindow class]])
+            [parent grabDockIconSnapshotFromWindow:self force:YES];
     }
 
     - (void) toggleFullScreen:(id)sender
@@ -2123,6 +2234,7 @@ static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modif
     - (void)windowWillMiniaturize:(NSNotification *)notification
     {
         [self becameIneligibleParentOrChild];
+        [self grabDockIconSnapshotFromWindow:nil force:NO];
     }
 
     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
@@ -2584,6 +2696,7 @@ void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
         {
             window.shape = nil;
             window.shapeData = nil;
+            [window checkEmptyShaped];
         }
         else
         {
@@ -2598,6 +2711,7 @@ void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
                     [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
                 window.shape = path;
                 window.shapeData = [NSData dataWithBytes:rects length:length];
+                [window checkEmptyShaped];
             }
         }
     });




More information about the wine-cvs mailing list