+// ----------------------------------------------------------------------------
+// window animation stuff
+// ----------------------------------------------------------------------------
+
+// define a delegate used to refresh the window during animation
+@interface wxNSAnimationDelegate : NSObject wxOSX_10_6_AND_LATER(<NSAnimationDelegate>)
+{
+ wxWindow *m_win;
+ bool m_isDone;
+}
+
+- (id)init:(wxWindow *)win;
+
+- (bool)isDone;
+
+// NSAnimationDelegate methods
+- (void)animationDidEnd:(NSAnimation*)animation;
+- (void)animation:(NSAnimation*)animation
+ didReachProgressMark:(NSAnimationProgress)progress;
+@end
+
+@implementation wxNSAnimationDelegate
+
+- (id)init:(wxWindow *)win
+{
+ self = [super init];
+
+ m_win = win;
+ m_isDone = false;
+
+ return self;
+}
+
+- (bool)isDone
+{
+ return m_isDone;
+}
+
+- (void)animation:(NSAnimation*)animation
+ didReachProgressMark:(NSAnimationProgress)progress
+{
+ wxUnusedVar(animation);
+ wxUnusedVar(progress);
+
+ m_win->SendSizeEvent();
+ m_win->MacOnInternalSize();
+}
+
+- (void)animationDidEnd:(NSAnimation*)animation
+{
+ wxUnusedVar(animation);
+ m_isDone = true;
+}
+
+@end
+
+/* static */
+bool
+wxWidgetCocoaImpl::ShowViewOrWindowWithEffect(wxWindow *win,
+ bool show,
+ wxShowEffect effect,
+ unsigned timeout)
+{
+ // 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
+ const wxRect rectOrig = win->GetRect();
+ wxRect rectStart,
+ rectEnd;
+ rectStart =
+ rectEnd = rectOrig;
+
+ 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();
+ }
+
+ 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
+ NSViewAnimation * const
+ anim = [[NSViewAnimation alloc]
+ initWithViewAnimations:[NSArray arrayWithObject:dict]];
+
+ 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];
+
+ wxNSAnimationDelegate * const
+ animDelegate = [[wxNSAnimationDelegate alloc] init:win];
+ [anim setDelegate:animDelegate];
+ [anim startAnimation];
+
+ // Cocoa is capable of doing animation asynchronously or even from separate
+ // thread but wx API doesn't provide any way to be notified about the
+ // animation end and without this we really must ensure that the window has
+ // the expected (i.e. the same as if a simple Show() had been used) size
+ // when we return, so block here until the animation finishes
+ //
+ // notice that because the default animation mode is NSAnimationBlocking,
+ // no user input events ought to be processed from here
+ {
+ wxEventLoopGuarantor ensureEventLoopExistence;
+ wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
+ while ( ![animDelegate isDone] )
+ loop->Dispatch();
+ }
+
+ if ( !show )
+ {
+ // NSViewAnimation is smart enough to hide the NSView being animated at
+ // the end but we also must ensure that it's hidden for wx too
+ win->Hide();
+
+ // and we must also restore its size because it isn't expected to
+ // change just because the window was hidden
+ win->SetSize(rectOrig);
+ }
+ else
+ {
+ // refresh it once again after the end to ensure that everything is in
+ // place
+ win->SendSizeEvent();
+ win->MacOnInternalSize();
+ }
+
+ [anim setDelegate:nil];
+ [animDelegate release];
+ [anim release];
+
+ return true;
+}
+
+bool wxWidgetCocoaImpl::ShowWithEffect(bool show,
+ wxShowEffect effect,
+ unsigned timeout)
+{
+ return ShowViewOrWindowWithEffect(m_wxPeer, show, effect, timeout);
+}
+
+/* note that the drawing order between siblings is not defined under 10.4 */
+/* only starting from 10.5 the subview order is respected */
+
+/* NSComparisonResult is typedef'd as an enum pre-Leopard but typedef'd as
+ * NSInteger post-Leopard. Pre-Leopard the Cocoa toolkit expects a function
+ * returning int and not NSComparisonResult. Post-Leopard the Cocoa toolkit
+ * expects a function returning the new non-enum NSComparsionResult.
+ * Hence we create a typedef named CocoaWindowCompareFunctionResult.
+ */
+#if defined(NSINTEGER_DEFINED)
+typedef NSComparisonResult CocoaWindowCompareFunctionResult;
+#else
+typedef int CocoaWindowCompareFunctionResult;
+#endif
+
+class CocoaWindowCompareContext
+{
+ wxDECLARE_NO_COPY_CLASS(CocoaWindowCompareContext);
+public:
+ CocoaWindowCompareContext(); // Not implemented
+ CocoaWindowCompareContext(NSView *target, NSArray *subviews)
+ {
+ m_target = target;
+ // Cocoa sorts subviews in-place.. make a copy
+ m_subviews = [subviews copy];
+ }
+
+ ~CocoaWindowCompareContext()
+ { // release the copy
+ [m_subviews release];
+ }
+ NSView* target()
+ { return m_target; }
+
+ NSArray* subviews()
+ { return m_subviews; }
+
+ /* Helper function that returns the comparison based off of the original ordering */
+ CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
+ {
+ NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
+ NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
+ // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
+ // likely compare higher than the other view which is reasonable considering the only way that
+ // can happen is if the subview was added after our call to subviews but before the call to
+ // sortSubviewsUsingFunction:context:. Thus we don't bother checking. Particularly because
+ // that case should never occur anyway because that would imply a multi-threaded GUI call
+ // which is a big no-no with Cocoa.
+
+ // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
+ NSComparisonResult result = (firstI < secondI)
+ ? NSOrderedAscending /* -1 */
+ : (firstI > secondI)
+ ? NSOrderedDescending /* 1 */
+ : NSOrderedSame /* 0 */;
+
+ return result;
+ }
+private:
+ /* The subview we are trying to Raise or Lower */
+ NSView *m_target;
+ /* A copy of the original array of subviews */
+ NSArray *m_subviews;
+};
+
+/* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
+ * the target view is always higher than every other view. When comparing two views neither of
+ * which is the target, it returns the correct response based on the original ordering
+ */
+static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
+{
+ CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
+ // first should be ordered higher
+ if(first==compareContext->target())
+ return NSOrderedDescending;
+ // second should be ordered higher
+ if(second==compareContext->target())
+ return NSOrderedAscending;
+ return compareContext->CompareUsingOriginalOrdering(first,second);
+}
+