]> git.saurik.com Git - wxWidgets.git/commitdiff
Implement wxWindow::ShowWithEffect() for wxOSX/Cocoa.
authorVadim Zeitlin <vadim@wxwidgets.org>
Mon, 5 Oct 2009 22:57:59 +0000 (22:57 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Mon, 5 Oct 2009 22:57:59 +0000 (22:57 +0000)
This version animates the window asynchronously and is being checked in just
to preserve it in svn if we later decide to return to this semantics. It will
be replaced by synchronous animation in the next commit.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62304 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
include/wx/osx/cocoa/private.h
include/wx/osx/core/private.h
include/wx/osx/nonownedwnd.h
include/wx/osx/window.h
interface/wx/window.h
src/osx/cocoa/nonownedwnd.mm
src/osx/cocoa/window.mm
src/osx/nonownedwnd_osx.cpp
src/osx/window_osx.cpp

index 5a34ed263b843569bfcbcd1f189f413ea668d1a6..b853af9f334bad4e698f8d34d1b4acbdec54b16f 100644 (file)
@@ -455,6 +455,7 @@ GTK:
 
 Mac:
 
+- Implement wxWindow::ShowWithEffect() in wxOSX/Cocoa.
 - Correct min/max pages display in the print dialog (Auria).
 
 MSW:
index 7b8b32008d9c1a86c5bd62fdccf7f7550cdc4bb3..a6ac933584924e8c49fefe91ea5f19b4a036956d 100644 (file)
@@ -73,6 +73,17 @@ public :
     virtual bool        IsVisible() const ;
     virtual void        SetVisibility(bool);
 
+    // we provide a static function which can be reused from
+    // wxNonOwnedWindowCocoaImpl too
+    static bool ShowViewOrWindowWithEffect(wxWindow *win,
+                                           bool show,
+                                           wxShowEffect effect,
+                                           unsigned timeout);
+
+    virtual bool ShowWithEffect(bool show,
+                                wxShowEffect effect,
+                                unsigned timeout);
+
     virtual void        Raise();
 
     virtual void        Lower();
@@ -190,7 +201,10 @@ public :
     void Raise();
     void Lower();
     bool Show(bool show);
-    bool ShowWithEffect(bool show, wxShowEffect effect, unsigned timeout);
+
+    virtual bool ShowWithEffect(bool show,
+                                wxShowEffect effect,
+                                unsigned timeout);
 
     void Update();
     bool SetTransparent(wxByte alpha);
index b6df304840f44bf76e8f3292f1f47556b3866798..418b6f8e4ac0b5ee83b2c46faaa0693a34d7a3c6 100644 (file)
@@ -188,6 +188,13 @@ public :
     // set the visibility of this widget (maybe latent)
     virtual void        SetVisibility( bool visible ) = 0;
 
+    virtual bool ShowWithEffect(bool WXUNUSED(show),
+                                wxShowEffect WXUNUSED(effect),
+                                unsigned WXUNUSED(timeout))
+    {
+        return false;
+    }
+
     virtual void        Raise() = 0;
 
     virtual void        Lower() = 0;
index b7b73e4e71b162926b338c8a5a8d4e6d6c1cd8d1..e650d7e094e9470e9ed8f9f81f51ec51b77cdba6 100644 (file)
@@ -85,12 +85,6 @@ public:
     virtual void Lower();
     virtual bool Show( bool show = true );
 
-    virtual bool ShowWithEffect(wxShowEffect effect,
-                                unsigned timeout = 0) ;
-
-    virtual bool HideWithEffect(wxShowEffect effect,
-                                unsigned timeout = 0) ;
-
     virtual void SetExtraStyle(long exStyle) ;
 
     virtual bool SetBackgroundColour( const wxColour &colour );
@@ -118,6 +112,10 @@ protected:
     virtual void DoMoveWindow(int x, int y, int width, int height);
     virtual void DoGetClientSize(int *width, int *height) const;
 
+    virtual bool OSXShowWithEffect(bool show,
+                                   wxShowEffect effect,
+                                   unsigned timeout);
+
     wxNonOwnedWindowImpl* m_nowpeer ;
 
 //    wxWindowMac* m_macFocus ;
index c7398555c907cb899590de4ffd6e7f987c171898..bdf48d8f74778af2baba84040fb1c0b421934cd2 100644 (file)
@@ -63,6 +63,16 @@ public:
     virtual void Lower();
 
     virtual bool Show( bool show = true );
+    virtual bool ShowWithEffect(wxShowEffect effect,
+                                unsigned timeout = 0)
+    {
+        return OSXShowWithEffect(true, effect, timeout);
+    }
+    virtual bool HideWithEffect(wxShowEffect effect,
+                                unsigned timeout = 0)
+    {
+        return OSXShowWithEffect(false, effect, timeout);
+    }
 
     virtual bool IsShownOnScreen() const;
 
@@ -341,6 +351,11 @@ protected:
     virtual void DoSetToolTip( wxToolTip *tip );
 #endif
 
+    // common part of Show/HideWithEffect()
+    virtual bool OSXShowWithEffect(bool show,
+                                   wxShowEffect effect,
+                                   unsigned timeout);
+
 private:
     // common part of all ctors
     void Init();
@@ -349,7 +364,6 @@ private:
     // AlwaysShowScrollbars()
     void DoUpdateScrollbarVisibility();
 
-
     wxDECLARE_NO_COPY_CLASS(wxWindowMac);
     DECLARE_EVENT_TABLE()
 };
index c3a1924e3c8458b68458a3846fead2152c861fb8..f0d02c7558e5fea3c3e9a16faa431904589c1c14 100644 (file)
@@ -2185,8 +2185,10 @@ public:
             milliseconds. If the default value of 0 is used, the default
             animation time for the current platform is used.
 
-        @note Currently this function is only implemented in wxMSW and does the
-              same thing as Show() in the other ports.
+        @note Currently this function is only implemented in wxMSW and wxOSX
+              (for wxTopLevelWindows only in Carbon version and for any kind of
+              windows in Cocoa) and does the same thing as Show() in the other
+              ports.
 
         @since 2.9.0
 
index 6e617f9bfdbb6a81983d20f1a4705700e75f96ff..c44f4eba89f42ff69bf02e5f52b92f7f65d513f7 100644 (file)
@@ -488,9 +488,12 @@ bool wxNonOwnedWindowCocoaImpl::Show(bool show)
     return true;
 }
 
-bool wxNonOwnedWindowCocoaImpl::ShowWithEffect(bool show, wxShowEffect WXUNUSED(effect), unsigned WXUNUSED(timeout))
+bool wxNonOwnedWindowCocoaImpl::ShowWithEffect(bool show,
+                                               wxShowEffect effect,
+                                               unsigned timeout)
 {
-    return Show(show);
+    return wxWidgetCocoaImpl::
+            ShowViewOrWindowWithEffect(m_wxPeer, show, effect, timeout);
 }
 
 void wxNonOwnedWindowCocoaImpl::Update()
index 9ce10cc02c172e1a1dc5a56f2472b884287a2b73..274eb120992e17021bf206e762ef2dd3fd918a7a 100644 (file)
@@ -21,6 +21,8 @@
     #include "wx/osx/private.h"
 #endif
 
+#include "wx/hashmap.h"
+
 #if wxUSE_CARET
     #include "wx/caret.h"
 #endif
 
 #include <objc/objc-runtime.h>
 
+namespace
+{
+
+// stop animation of this window if one is in progress
+void StopAnimation(wxWindow *win);
+
+} // anonymous namespace
+
+
 // Get the window with the focus
 
 NSView* GetViewFromResponder( NSResponder* responder )
@@ -1169,6 +1180,8 @@ void wxWidgetCocoaImpl::Init()
 
 wxWidgetCocoaImpl::~wxWidgetCocoaImpl()
 {
+    StopAnimation(m_wxPeer);
+
     RemoveAssociations( this );
 
     if ( !IsRootControl() )
@@ -1192,6 +1205,286 @@ void wxWidgetCocoaImpl::SetVisibility( bool visible )
     [m_osxView setHidden:(visible ? NO:YES)];
 }
 
+// ----------------------------------------------------------------------------
+// window animation stuff
+// ----------------------------------------------------------------------------
+
+namespace
+{
+
+WX_DECLARE_VOIDPTR_HASH_MAP(NSViewAnimation *, wxNSViewAnimations);
+
+// all currently active animations
+//
+// this is MT-safe because windows can only be animated from the main
+// thread anyhow
+wxNSViewAnimations gs_activeAnimations;
+
+void StopAnimation(wxWindow *win)
+{
+    wxNSViewAnimations::iterator it = gs_activeAnimations.find(win);
+    if ( it != gs_activeAnimations.end() )
+    {
+        [it->second stopAnimation];
+    }
+}
+
+} // anonymous namespace
+
+// define a delegate used to detect the end of the window animation
+@interface wxNSAnimationDelegate : NSObject
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+                                   <NSAnimationDelegate>
+#endif
+{
+    // can't use wxRect here as it has a user-defined ctor and so can't be used
+    // as an Objective-C field
+    struct
+    {
+        int x, y, w, h;
+    } m_origRect;
+    wxWindow *m_win;
+    bool m_show;
+}
+
+- (id)initWithWindow:(wxWindow *)win show:(bool)show;
+
+// NSAnimationDelegate methods
+- (void)animation:(NSAnimation*)animation
+        didReachProgressMark:(NSAnimationProgress)progress;
+- (void)animationDidStop:(NSAnimation *)animation;
+- (void)animationDidEnd:(NSAnimation *)animation;
+
+// private helpers
+- (void)finishAnimation:(NSAnimation *)animation;
+@end
+
+@implementation wxNSAnimationDelegate
+
+- (id)initWithWindow:(wxWindow *)win show:(bool)show
+{
+    [super init];
+
+    m_win = win;
+    m_show = show;
+    if ( !show )
+    {
+        m_win->GetPosition(&m_origRect.x, &m_origRect.y);
+        m_win->GetSize(&m_origRect.w, &m_origRect.h);
+    }
+    return self;
+}
+
+- (void)animation:(NSAnimation*)animation
+        didReachProgressMark:(NSAnimationProgress)progress
+{
+    wxUnusedVar(animation);
+    wxUnusedVar(progress);
+
+    m_win->SendSizeEvent();
+}
+
+- (void)animationDidStop:(NSAnimation *)animation
+{
+    [self finishAnimation:animation];
+}
+
+- (void)animationDidEnd:(NSAnimation *)animation
+{
+    [self finishAnimation:animation];
+}
+
+- (void)finishAnimation:(NSAnimation *)animation
+{
+    if ( m_show )
+    {
+        // the window expects to be sent a size event when it is shown normally
+        // and so it should also get one when it is shown with effect
+        m_win->SendSizeEvent();
+    }
+    else // window was being hidden
+    {
+        // NSViewAnimation is smart enough to hide the NSView itself but we
+        // also need to ensure that it's considered to be hidden at wx level
+        m_win->Hide();
+
+        // and we also need to restore its original size which was changed by
+        // the animation
+        m_win->SetSize(m_origRect.x, m_origRect.y, m_origRect.w, m_origRect.h);
+    }
+
+    wxNSViewAnimations::iterator it = gs_activeAnimations.find(m_win);
+    wxASSERT_MSG( it != gs_activeAnimations.end() && it->second == animation,
+                  "corrupted active animations list?" );
+
+    gs_activeAnimations.erase(it);
+
+    // we don't dare to release it immediately as we're called from its code
+    // but schedule the animation itself for deletion soon
+    [animation autorelease];
+
+    // ensure that this delegate is definitely not needed any more before
+    // destroying it
+    [animation setDelegate:nil];
+    [self release];
+}
+
+@end
+
+/* static */
+bool
+wxWidgetCocoaImpl::ShowViewOrWindowWithEffect(wxWindow *win,
+                                              bool show,
+                                              wxShowEffect effect,
+                                              unsigned timeout)
+{
+    // first of all, check if this window is not being already animated and
+    // cancel the previous animation if it is as performing more than one
+    // animation on the same window at the same time results in some really
+    // unexpected effects
+    StopAnimation(win);
+
+
+    // create the dictionary describing the animation to perform on this view
+    NSObject * const
+        viewOrWin = static_cast<NSObject *>(win->OSXGetViewOrWindow());
+    NSMutableDictionary * const
+        dict = [NSMutableDictionary dictionaryWithCapacity:4];
+    [dict setObject:viewOrWin forKey:NSViewAnimationTargetKey];
+
+    // determine the start and end rectangles assuming we're hiding the window
+    wxRect rectStart,
+           rectEnd;
+    rectStart =
+    rectEnd = win->GetRect();
+
+    if ( show )
+    {
+        if ( effect == wxSHOW_EFFECT_ROLL_TO_LEFT ||
+                effect == wxSHOW_EFFECT_SLIDE_TO_LEFT )
+            effect = wxSHOW_EFFECT_ROLL_TO_RIGHT;
+        else if ( effect == wxSHOW_EFFECT_ROLL_TO_RIGHT ||
+                    effect == wxSHOW_EFFECT_SLIDE_TO_RIGHT )
+            effect = wxSHOW_EFFECT_ROLL_TO_LEFT;
+        else if ( effect == wxSHOW_EFFECT_ROLL_TO_TOP ||
+                    effect == wxSHOW_EFFECT_SLIDE_TO_TOP )
+            effect = wxSHOW_EFFECT_ROLL_TO_BOTTOM;
+        else if ( effect == wxSHOW_EFFECT_ROLL_TO_BOTTOM ||
+                    effect == wxSHOW_EFFECT_SLIDE_TO_BOTTOM )
+            effect = wxSHOW_EFFECT_ROLL_TO_TOP;
+    }
+
+    switch ( effect )
+    {
+        case wxSHOW_EFFECT_ROLL_TO_LEFT:
+        case wxSHOW_EFFECT_SLIDE_TO_LEFT:
+            rectEnd.width = 0;
+            break;
+
+        case wxSHOW_EFFECT_ROLL_TO_RIGHT:
+        case wxSHOW_EFFECT_SLIDE_TO_RIGHT:
+            rectEnd.x = rectStart.GetRight();
+            rectEnd.width = 0;
+            break;
+
+        case wxSHOW_EFFECT_ROLL_TO_TOP:
+        case wxSHOW_EFFECT_SLIDE_TO_TOP:
+            rectEnd.height = 0;
+            break;
+
+        case wxSHOW_EFFECT_ROLL_TO_BOTTOM:
+        case wxSHOW_EFFECT_SLIDE_TO_BOTTOM:
+            rectEnd.y = rectStart.GetBottom();
+            rectEnd.height = 0;
+            break;
+
+        case wxSHOW_EFFECT_EXPAND:
+            rectEnd.x = rectStart.x + rectStart.width / 2;
+            rectEnd.y = rectStart.y + rectStart.height / 2;
+            rectEnd.width =
+            rectEnd.height = 0;
+            break;
+
+        case wxSHOW_EFFECT_BLEND:
+            [dict setObject:(show ? NSViewAnimationFadeInEffect
+                                  : NSViewAnimationFadeOutEffect)
+                  forKey:NSViewAnimationEffectKey];
+            break;
+
+        case wxSHOW_EFFECT_NONE:
+        case wxSHOW_EFFECT_MAX:
+            wxFAIL_MSG( "unexpected animation effect" );
+            return false;
+
+        default:
+            wxFAIL_MSG( "unknown animation effect" );
+            return false;
+    };
+
+    if ( show )
+    {
+        // we need to restore it to the original rectangle instead of making it
+        // disappear
+        wxSwap(rectStart, rectEnd);
+
+        // and as the window is currently hidden, we need to show it for the
+        // animation to be visible at all (but don't restore it at its full
+        // rectangle as it shouldn't appear immediately)
+        win->SetSize(rectStart);
+        win->Show(true);
+    }
+
+    NSView * const parentView = [viewOrWin isKindOfClass:[NSView class]]
+                                    ? [(NSView *)viewOrWin superview]
+                                    : nil;
+    const NSRect rStart = wxToNSRect(parentView, rectStart);
+    const NSRect rEnd = wxToNSRect(parentView, rectEnd);
+
+    [dict setObject:[NSValue valueWithRect:rStart]
+          forKey:NSViewAnimationStartFrameKey];
+    [dict setObject:[NSValue valueWithRect:rEnd]
+          forKey:NSViewAnimationEndFrameKey];
+
+    // create an animation using the values in the above dictionary
+    //
+    // notice that it will be released when it is removed from
+    // gs_activeAnimations
+    NSViewAnimation * const
+        anim = [[NSViewAnimation alloc]
+                initWithViewAnimations:[NSArray arrayWithObject:dict]];
+    gs_activeAnimations[win] = anim;
+
+    if ( !timeout )
+    {
+        // what is a good default duration? Windows uses 200ms, Web frameworks
+        // use anything from 250ms to 1s... choose something in the middle
+        timeout = 500;
+    }
+
+    [anim setDuration:timeout/1000.];   // duration is in seconds here
+
+    // if the window being animated changes its layout depending on its size
+    // (which is almost always the case) we need to redo it during animation
+    //
+    // the number of layouts here is arbitrary, but 10 seems like too few (e.g.
+    // controls in wxInfoBar visibly jump around)
+    const int NUM_LAYOUTS = 20;
+    for ( float f = 1./NUM_LAYOUTS; f < 1.; f += 1./NUM_LAYOUTS )
+        [anim addProgressMark:f];
+
+    [anim setDelegate:[[wxNSAnimationDelegate alloc] initWithWindow:win show:show]];
+    [anim startAnimation];
+
+    return true;
+}
+
+bool wxWidgetCocoaImpl::ShowWithEffect(bool show,
+                                       wxShowEffect effect,
+                                       unsigned timeout)
+{
+    return ShowViewOrWindowWithEffect(m_wxPeer, show, effect, timeout);
+}
+
 void wxWidgetCocoaImpl::Raise()
 {
     // Not implemented
index 7b8f08cedd59ace147a2ba038d9ccf585199f046..fc50ea40916ec9d6fa249d5ab386609d7ef6c7d1 100644 (file)
@@ -169,28 +169,32 @@ wxNonOwnedWindow::~wxNonOwnedWindow()
 // wxNonOwnedWindow misc
 // ----------------------------------------------------------------------------
 
-bool wxNonOwnedWindow::ShowWithEffect(wxShowEffect effect,
-                                unsigned timeout )
+bool wxNonOwnedWindow::OSXShowWithEffect(bool show,
+                                         wxShowEffect effect,
+                                         unsigned timeout)
 {
-    if ( !wxWindow::Show(true) )
+    // Cocoa code needs to manage window visibility on its own and so calls
+    // wxWindow::Show() as needed but if we already changed the internal
+    // visibility flag here, Show() would do nothing, so avoid doing it
+#if wxOSX_USE_CARBON
+    if ( !wxWindow::Show(show) )
         return false;
+#endif // Carbon
 
-    // because apps expect a size event to occur at this moment
-    wxSizeEvent event(GetSize() , m_windowId);
-    event.SetEventObject(this);
-    HandleWindowEvent(event);
-
+    if ( effect == wxSHOW_EFFECT_NONE ||
+            !m_nowpeer || !m_nowpeer->ShowWithEffect(show, effect, timeout) )
+        return Show(show);
 
-    return m_nowpeer->ShowWithEffect(true, effect, timeout);
-}
-
-bool wxNonOwnedWindow::HideWithEffect(wxShowEffect effect,
-                                unsigned timeout )
-{
-    if ( !wxWindow::Show(false) )
-        return false;
+    if ( show )
+    {
+        // as apps expect a size event to occur when the window is shown,
+        // generate one when it is shown with effect too
+        wxSizeEvent event(GetSize(), m_windowId);
+        event.SetEventObject(this);
+        HandleWindowEvent(event);
+    }
 
-    return m_nowpeer->ShowWithEffect(false, effect, timeout);
+    return true;
 }
 
 wxPoint wxNonOwnedWindow::GetClientAreaOrigin() const
index 3d355fde51783d2863fb7fc162ec5e8e410ec07b..42e856aaec006e99e33d350490a9b06dff61c164 100644 (file)
@@ -1063,6 +1063,17 @@ bool wxWindowMac::Show(bool show)
     return true;
 }
 
+bool wxWindowMac::OSXShowWithEffect(bool show,
+                                    wxShowEffect effect,
+                                    unsigned timeout)
+{
+    if ( effect == wxSHOW_EFFECT_NONE ||
+            !m_peer || !m_peer->ShowWithEffect(show, effect, timeout) )
+        return Show(show);
+
+    return true;
+}
+
 void wxWindowMac::DoEnable(bool enable)
 {
     m_peer->Enable( enable ) ;