Implement wxWindow::ShowWithEffect() for wxOSX/Cocoa.

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
This commit is contained in:
Vadim Zeitlin 2009-10-05 22:57:59 +00:00
parent ff6893d2c2
commit ab9a0b84de
10 changed files with 376 additions and 29 deletions

View File

@ -455,6 +455,7 @@ GTK:
Mac:
- Implement wxWindow::ShowWithEffect() in wxOSX/Cocoa.
- Correct min/max pages display in the print dialog (Auria).
MSW:

View 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);

View 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;

View 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 ;

View 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()
};

View 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

View 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()

View File

@ -21,6 +21,8 @@
#include "wx/osx/private.h"
#endif
#include "wx/hashmap.h"
#if wxUSE_CARET
#include "wx/caret.h"
#endif
@ -31,6 +33,15 @@
#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

View 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);
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(true, effect, timeout);
}
bool wxNonOwnedWindow::HideWithEffect(wxShowEffect effect,
unsigned timeout )
{
if ( !wxWindow::Show(false) )
return false;
return m_nowpeer->ShowWithEffect(false, effect, timeout);
return true;
}
wxPoint wxNonOwnedWindow::GetClientAreaOrigin() const

View 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 ) ;