]> git.saurik.com Git - wxWidgets.git/blobdiff - src/osx/cocoa/window.mm
Start on Get/SetStyle support for OS X Cocoa wxTextCtrl.
[wxWidgets.git] / src / osx / cocoa / window.mm
index 7bf085d65b4190e22a534f651f56036045f77262..59b06c142f1e83a98706f45f6bea1bc5e92ebb06 100644 (file)
@@ -21,6 +21,8 @@
     #include "wx/osx/private.h"
 #endif
 
+#include "wx/evtloop.h"
+
 #if wxUSE_CARET
     #include "wx/caret.h"
 #endif
@@ -114,6 +116,8 @@ NSRect wxOSXGetFrameForControl( wxWindowMac* window , const wxPoint& pos , const
 - (void)setTarget:(id)anObject;
 - (void)setAction:(SEL)aSelector;
 - (void)setDoubleAction:(SEL)aSelector;
+- (void)setBackgroundColor:(NSColor*)aColor;
+- (void)setImagePosition:(NSCellImagePosition)aPosition;
 @end
 
 long wxOSXTranslateCocoaKey( NSEvent* event )
@@ -271,7 +275,7 @@ long wxOSXTranslateCocoaKey( NSEvent* event )
     return retval;
 }
 
-void SetupKeyEvent( wxKeyEvent &wxevent , NSEvent * nsEvent, NSString* charString = NULL )
+void wxWidgetCocoaImpl::SetupKeyEvent(wxKeyEvent &wxevent , NSEvent * nsEvent, NSString* charString)
 {
     UInt32 modifiers = [nsEvent modifierFlags] ;
     int eventType = [nsEvent type];
@@ -350,12 +354,19 @@ void SetupKeyEvent( wxKeyEvent &wxevent , NSEvent * nsEvent, NSString* charStrin
     wxevent.m_uniChar = aunichar;
 #endif
     wxevent.m_keyCode = keyval;
+
+    wxWindowMac* peer = GetWXPeer();
+    if ( peer )
+    {
+        wxevent.SetEventObject(peer);
+        wxevent.SetId(peer->GetId()) ;
+    }
 }
 
 UInt32 g_lastButton = 0 ;
 bool g_lastButtonWasFakeRight = false ;
 
-void SetupMouseEvent( wxMouseEvent &wxevent , NSEvent * nsEvent )
+void wxWidgetCocoaImpl::SetupMouseEvent( wxMouseEvent &wxevent , NSEvent * nsEvent )
 {
     int eventType = [nsEvent type];
     UInt32 modifiers = [nsEvent modifierFlags] ;
@@ -521,7 +532,12 @@ void SetupMouseEvent( wxMouseEvent &wxevent , NSEvent * nsEvent )
     }
 
     wxevent.m_clickCount = clickCount;
-
+    wxWindowMac* peer = GetWXPeer();
+    if ( peer )
+    {
+        wxevent.SetEventObject(peer);
+        wxevent.SetId(peer->GetId()) ;
+    }
 }
 
 @implementation wxNSView
@@ -869,11 +885,14 @@ void wxWidgetCocoaImpl::mouseEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
 
 void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
 {
+    if ( [event type] == NSKeyDown )
+        m_lastKeyDownEvent = event;
     if ( GetFocusedViewInWindow([slf window]) != slf || m_hasEditor || !DoHandleKeyEvent(event) )
     {
         wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
         superimpl(slf, (SEL)_cmd, event);
     }
+    m_lastKeyDownEvent = NULL;
 }
 
 void wxWidgetCocoaImpl::insertText(NSString* text, WXWidget slf, void *_cmd)
@@ -883,7 +902,6 @@ void wxWidgetCocoaImpl::insertText(NSString* text, WXWidget slf, void *_cmd)
         wxOSX_TextEventHandlerPtr superimpl = (wxOSX_TextEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
         superimpl(slf, (SEL)_cmd, text);
     }
-    m_lastKeyDownEvent = NULL;
 }
 
 
@@ -1176,6 +1194,245 @@ void wxWidgetCocoaImpl::SetVisibility( bool visible )
     [m_osxView setHidden:(visible ? NO:YES)];
 }
 
+// ----------------------------------------------------------------------------
+// window animation stuff
+// ----------------------------------------------------------------------------
+
+// define a delegate used to refresh the window during animation
+@interface wxNSAnimationDelegate : NSObject
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+                                   <NSAnimationDelegate>
+#endif
+{
+    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
+{
+    [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();
+}
+
+- (void)animationDidEnd:(NSAnimation*)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();
+    }
+
+    [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);
+}
+
 void wxWidgetCocoaImpl::Raise()
 {
     // Not implemented
@@ -1316,9 +1573,19 @@ void wxWidgetCocoaImpl::Embed( wxWidgetImpl *parent )
     [container addSubview:m_osxView];
 }
 
-void wxWidgetCocoaImpl::SetBackgroundColour( const wxColour &WXUNUSED(col) )
+void wxWidgetCocoaImpl::SetBackgroundColour( const wxColour &col )
 {
-    // m_osxView.backgroundColor = [[UIColor alloc] initWithCGColor:col.GetCGColor()];
+    NSView* targetView = m_osxView;
+    if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
+        targetView = [(NSScrollView*) m_osxView documentView];
+
+    if ( [targetView respondsToSelector:@selector(setBackgroundColor:) ] )
+    {
+        [targetView setBackgroundColor:[NSColor colorWithCalibratedRed:(CGFloat) (col.Red() / 255.0)
+                                                                green:(CGFloat) (col.Green() / 255.0)
+                                                                 blue:(CGFloat) (col.Blue() / 255.0)
+                                                                alpha:(CGFloat) (col.Alpha() / 255.0)]];
+    }
 }
 
 void wxWidgetCocoaImpl::SetLabel( const wxString& title, wxFontEncoding encoding )
@@ -1416,6 +1683,7 @@ void wxWidgetCocoaImpl::SetBitmap( const wxBitmap& bitmap )
     if (  [m_osxView respondsToSelector:@selector(setImage:)] )
     {
         [m_osxView setImage:bitmap.GetNSImage()];
+        [m_osxView setNeedsDisplay:YES];
     }
 }
 
@@ -1554,7 +1822,6 @@ bool wxWidgetCocoaImpl::DoHandleCharEvent(NSEvent *event, NSString *text)
 {
     wxKeyEvent wxevent(wxEVT_CHAR);
     SetupKeyEvent( wxevent, event, text );
-    wxevent.SetEventObject(GetWXPeer());
 
     return GetWXPeer()->OSXHandleKeyEvent(wxevent);
 }
@@ -1563,14 +1830,13 @@ bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)
 {
     wxKeyEvent wxevent(wxEVT_KEY_DOWN);
     SetupKeyEvent( wxevent, event );
-    wxevent.SetEventObject(GetWXPeer());
     bool result = GetWXPeer()->OSXHandleKeyEvent(wxevent);
 
     // this will fire higher level events, like insertText, to help
     // us handle EVT_CHAR, etc.
-    if ( !m_hasEditor && [event type] == NSKeyDown)
+
+    if ( m_wxPeer->MacIsUserPane() && [event type] == NSKeyDown)
     {
-        m_lastKeyDownEvent = event;
         if ( !result )
         {
             if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
@@ -1580,6 +1846,7 @@ bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)
             result = true;
         }
     }
+
     return result;
 }
 
@@ -1589,8 +1856,7 @@ bool wxWidgetCocoaImpl::DoHandleMouseEvent(NSEvent *event)
     clickLocation = [m_osxView convertPoint:[event locationInWindow] fromView:nil];
     wxPoint pt = wxFromNSPoint( m_osxView, clickLocation );
     wxMouseEvent wxevent(wxEVT_LEFT_DOWN);
-    SetupMouseEvent( wxevent , event ) ;
-    wxevent.SetEventObject(GetWXPeer());
+    SetupMouseEvent(wxevent , event) ;
     wxevent.m_x = pt.x;
     wxevent.m_y = pt.y;