1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/iphone/window.mm
3 // Purpose: widgets (non tlw) for iphone
4 // Author: Stefan Csomor
7 // RCS-ID: $Id: window.mm 48805 2007-09-19 14:52:25Z SC $
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
14 #include "wx/osx/private.h"
17 #include "wx/nonownedwnd.h"
23 #include <objc/runtime.h>
25 WXWidget wxWidgetImpl::FindFocus()
27 UIView* focusedView = nil;
28 UIWindow* keyWindow = [[UIApplication sharedApplication] keyWindow];
29 if ( keyWindow != nil )
32 NSResponder* responder = [keyWindow firstResponder];
33 if ( [responder isKindOfClass:[NSTextView class]] &&
34 [keyWindow fieldEditor:NO forObject:nil] != nil )
36 focusedView = [(NSTextView*)responder delegate];
40 if ( [responder isKindOfClass:[NSView class]] )
41 focusedView = (NSView*) responder;
48 CGRect wxOSXGetFrameForControl( wxWindowMac* window , const wxPoint& pos , const wxSize &size , bool adjustForOrigin )
52 window->MacGetBoundsForControl( pos , size , x , y, w, h , adjustForOrigin ) ;
53 wxRect bounds(x,y,w,h);
54 UIView* sv = (window->GetParent()->GetHandle() );
56 return wxToNSRect( sv, bounds );
60 @interface wxUIView(PossibleMethods)
61 - (void)setTitle:(NSString *)title forState:(UIControlState)state;
63 - (void)drawRect: (CGRect) rect;
65 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
66 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
67 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
68 - (void)handleTouchEvent:(NSSet *)touches withEvent:(UIEvent *)event;
70 - (BOOL) becomeFirstResponder;
71 - (BOOL) resignFirstResponder;
78 void SetupMouseEvent( wxMouseEvent &wxevent , NSSet* touches, UIEvent * nsEvent )
80 UInt32 modifiers = 0 ;
81 UITouch *touch = [touches anyObject];
83 // these parameters are not given for all events
84 UInt32 button = 0; // no secondary button
85 UInt32 clickCount = [touch tapCount];
86 UInt32 mouseChord = 0; // TODO does this exist for cocoa
91 wxevent.m_shiftDown = 0;
92 wxevent.m_controlDown = 0;
93 wxevent.m_altDown = 0;
94 wxevent.m_metaDown = 0;
95 wxevent.m_clickCount = clickCount;
96 wxevent.SetTimestamp( [touch timestamp] ) ;
98 // a control click is interpreted as a right click
99 bool thisButtonIsFakeRight = false ;
100 if ( button == kEventMouseButtonPrimary && (modifiers & controlKey) )
102 button = kEventMouseButtonSecondary ;
103 thisButtonIsFakeRight = true ;
106 // otherwise we report double clicks by connecting a left click with a ctrl-left click
107 if ( clickCount > 1 && button != g_lastButton )
109 // we must make sure that our synthetic 'right' button corresponds in
110 // mouse down, moved and mouse up, and does not deliver a right down and left up
112 if ( cEvent.GetKind() == kEventMouseDown )
114 g_lastButton = button ;
115 g_lastButtonWasFakeRight = thisButtonIsFakeRight ;
121 g_lastButtonWasFakeRight = false ;
123 else if ( g_lastButton == kEventMouseButtonSecondary && g_lastButtonWasFakeRight )
124 button = g_lastButton ;
126 // Adjust the chord mask to remove the primary button and add the
127 // secondary button. It is possible that the secondary button is
128 // already pressed, e.g. on a mouse connected to a laptop, but this
129 // possibility is ignored here:
130 if( thisButtonIsFakeRight && ( mouseChord & 1U ) )
131 mouseChord = ((mouseChord & ~1U) | 2U);
134 wxevent.m_leftDown = true ;
136 wxevent.m_rightDown = true ;
138 wxevent.m_middleDown = true ;
141 // translate into wx types
142 int eventType = [touch phase];
145 case UITouchPhaseBegan :
149 wxevent.SetEventType( clickCount > 1 ? wxEVT_LEFT_DCLICK : wxEVT_LEFT_DOWN ) ;
157 case UITouchPhaseEnded :
161 wxevent.SetEventType( wxEVT_LEFT_UP ) ;
169 case UITouchPhaseMoved :
170 wxevent.SetEventType( wxEVT_MOTION ) ;
177 @implementation wxUIView
181 static BOOL initialized = NO;
185 wxOSXIPhoneClassAddWXMethods( self );
192 - (void)drawRect: (CGRect) rect
194 wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
197 CGContextRef context = (CGContextRef) UIGraphicsGetCurrentContext();
198 CGContextSaveGState( context );
201 CGContextSetFillColorWithColor( context, impl->GetWXPeer()->GetBackgroundColour().GetCGColor());
202 CGContextFillRect(context, rect );
204 impl->GetWXPeer()->MacSetCGContextRef( context );
206 impl->GetWXPeer()->GetUpdateRegion() =
207 wxRegion(rect.origin.x,rect.origin.y,rect.size.width,rect.size.height) ;
210 event.SetTimestamp(0); // todo
211 event.SetEventObject(impl->GetWXPeer());
212 impl->GetWXPeer()->HandleWindowEvent(event);
214 CGContextRestoreGState( context );
219 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
221 [self handleTouchEvent:touches withEvent:event];
224 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
226 [self handleTouchEvent:touches withEvent:event];
229 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
231 [self handleTouchEvent:touches withEvent:event];
234 -(void)handleTouchEvent:(NSSet *)touches withEvent:(UIEvent *)event
236 wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
237 CGPoint clickLocation;
238 UITouch *touch = [touches anyObject];
239 clickLocation = [touch locationInView:self];
241 wxMouseEvent wxevent(wxEVT_LEFT_DOWN);
242 SetupMouseEvent( wxevent , touches, event ) ;
243 wxevent.m_x = clickLocation.x;
244 wxevent.m_y = clickLocation.y;
245 wxevent.SetEventObject( impl->GetWXPeer() ) ;
246 wxevent.SetId( impl->GetWXPeer()->GetId() ) ;
247 impl->GetWXPeer()->HandleWindowEvent(wxevent);
252 void wxOSX_touchEvent(UIView* self, SEL _cmd, NSSet* touches, UIEvent *event )
254 wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
258 impl->touchEvent(touches, event, self, _cmd);
261 BOOL wxOSX_becomeFirstResponder(UIView* self, SEL _cmd)
263 wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
267 return impl->becomeFirstResponder(self, _cmd);
270 BOOL wxOSX_resignFirstResponder(UIView* self, SEL _cmd)
272 wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
276 return impl->resignFirstResponder(self, _cmd);
279 void wxOSX_drawRect(UIView* self, SEL _cmd, CGRect rect)
281 wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
285 return impl->drawRect(&rect, self, _cmd);
289 void wxOSXIPhoneClassAddWXMethods(Class c)
291 class_addMethod(c, @selector(touchesBegan:withEvent:), (IMP) wxOSX_touchEvent, "v@:@@");
292 class_addMethod(c, @selector(touchesMoved:withEvent:), (IMP) wxOSX_touchEvent, "v@:@@");
293 class_addMethod(c, @selector(touchesEnded:withEvent:), (IMP) wxOSX_touchEvent, "v@:@@");
294 class_addMethod(c, @selector(becomeFirstResponder), (IMP) wxOSX_becomeFirstResponder, "c@:" );
295 class_addMethod(c, @selector(resignFirstResponder), (IMP) wxOSX_resignFirstResponder, "c@:" );
296 class_addMethod(c, @selector(drawRect:), (IMP) wxOSX_drawRect, "v@:{_CGRect={_CGPoint=ff}{_CGSize=ff}}" );
300 IMPLEMENT_DYNAMIC_CLASS( wxWidgetIPhoneImpl , wxWidgetImpl )
302 wxWidgetIPhoneImpl::wxWidgetIPhoneImpl( wxWindowMac* peer , WXWidget w, bool isRootControl ) :
303 wxWidgetImpl( peer, isRootControl ), m_osxView(w)
307 wxWidgetIPhoneImpl::wxWidgetIPhoneImpl()
311 void wxWidgetIPhoneImpl::Init()
316 wxWidgetIPhoneImpl::~wxWidgetIPhoneImpl()
318 RemoveAssociations( this );
320 if ( !IsRootControl() )
322 UIView *sv = [m_osxView superview];
324 [m_osxView removeFromSuperview];
329 bool wxWidgetIPhoneImpl::IsVisible() const
331 UIView* view = m_osxView;
332 while ( view != nil && [view isHidden] == NO )
334 view = [view superview];
339 void wxWidgetIPhoneImpl::SetVisibility( bool visible )
341 [m_osxView setHidden:(visible ? NO:YES)];
344 void wxWidgetIPhoneImpl::Raise()
346 [[m_osxView superview] bringSubviewToFront:m_osxView];
349 void wxWidgetIPhoneImpl::Lower()
351 [[m_osxView superview] sendSubviewToBack:m_osxView];
354 void wxWidgetIPhoneImpl::ScrollRect( const wxRect *rect, int dx, int dy )
359 void wxWidgetIPhoneImpl::Move(int x, int y, int width, int height)
361 CGRect r = CGRectMake( x, y, width, height) ;
362 [m_osxView setFrame:r];
365 void wxWidgetIPhoneImpl::GetPosition( int &x, int &y ) const
367 CGRect r = [m_osxView frame];
372 void wxWidgetIPhoneImpl::GetSize( int &width, int &height ) const
374 CGRect rect = [m_osxView frame];
375 width = rect.size.width;
376 height = rect.size.height;
379 void wxWidgetIPhoneImpl::GetContentArea( int&left, int &top, int &width, int &height ) const
382 CGRect rect = [m_osxView bounds];
383 width = rect.size.width;
384 height = rect.size.height;
387 void wxWidgetIPhoneImpl::SetNeedsDisplay( const wxRect* where )
391 CGRect r = CGRectMake( where->x, where->y, where->width, where->height) ;
392 [m_osxView setNeedsDisplayInRect:r];
395 [m_osxView setNeedsDisplay];
398 bool wxWidgetIPhoneImpl::GetNeedsDisplay() const
401 // return [m_osxView needsDisplay];
404 bool wxWidgetIPhoneImpl::CanFocus() const
406 return [m_osxView canBecomeFirstResponder] == YES;
407 // ? return [m_osxView isUserInteractionEnabled] == YES;
410 bool wxWidgetIPhoneImpl::HasFocus() const
412 return [m_osxView isFirstResponder] == YES;
415 bool wxWidgetIPhoneImpl::SetFocus()
417 // [m_osxView makeKeyWindow] ;
423 void wxWidgetIPhoneImpl::RemoveFromParent()
425 [m_osxView removeFromSuperview];
428 void wxWidgetIPhoneImpl::Embed( wxWidgetImpl *parent )
430 UIView* container = parent->GetWXWidget() ;
431 wxASSERT_MSG( container != NULL , wxT("No valid mac container control") ) ;
432 [container addSubview:m_osxView];
435 void wxWidgetImpl::Convert( wxPoint *pt , wxWidgetImpl *from , wxWidgetImpl *to )
437 CGPoint p = CGPointMake( pt->x , pt->y );
438 p = [from->GetWXWidget() convertPoint:p toView:to->GetWXWidget() ];
443 void wxWidgetIPhoneImpl::SetBackgroundColour( const wxColour &col )
445 m_osxView.backgroundColor = [[UIColor alloc] initWithCGColor:col.GetCGColor()];
448 void wxWidgetIPhoneImpl::SetLabel(const wxString& title, wxFontEncoding encoding)
450 if ( [m_osxView respondsToSelector:@selector(setTitle:forState:) ] )
452 wxCFStringRef cf( title , encoding );
453 [m_osxView setTitle:cf.AsNSString() forState:UIControlStateNormal ];
455 else if ( [m_osxView respondsToSelector:@selector(setStringValue:) ] )
457 wxCFStringRef cf( title , encoding );
458 [m_osxView setStringValue:cf.AsNSString()];
463 void wxWidgetIPhoneImpl::SetCursor( const wxCursor & cursor )
467 void wxWidgetIPhoneImpl::CaptureMouse()
471 void wxWidgetIPhoneImpl::ReleaseMouse()
475 wxInt32 wxWidgetIPhoneImpl::GetValue() const
479 void wxWidgetIPhoneImpl::SetValue( wxInt32 v )
483 void wxWidgetIPhoneImpl::SetBitmap( const wxBitmap& bitmap )
487 wxBitmap wxWidgetIPhoneImpl::GetBitmap() const
493 void wxWidgetIPhoneImpl::SetBitmapPosition( wxDirection dir )
497 void wxWidgetIPhoneImpl::SetupTabs( const wxNotebook ¬ebook )
501 void wxWidgetIPhoneImpl::GetBestRect( wxRect *r ) const
503 r->x = r->y = r->width = r->height = 0;
505 if ( [m_osxView respondsToSelector:@selector(sizeToFit)] )
507 CGRect former = [m_osxView frame];
508 [m_osxView sizeToFit];
509 CGRect best = [m_osxView frame];
510 [m_osxView setFrame:former];
511 r->width = best.size.width;
512 r->height = best.size.height;
516 bool wxWidgetIPhoneImpl::IsEnabled() const
520 void wxWidgetIPhoneImpl::Enable( bool enable )
524 void wxWidgetIPhoneImpl::SetMinimum( wxInt32 v )
528 void wxWidgetIPhoneImpl::SetMaximum( wxInt32 v )
532 wxInt32 wxWidgetIPhoneImpl::GetMinimum() const
536 wxInt32 wxWidgetIPhoneImpl::GetMaximum() const
540 void wxWidgetIPhoneImpl::PulseGauge()
544 void wxWidgetIPhoneImpl::SetScrollThumb( wxInt32 value, wxInt32 thumbSize )
548 void wxWidgetIPhoneImpl::SetControlSize( wxWindowVariant variant )
552 void wxWidgetIPhoneImpl::SetFont( const wxFont & font , const wxColour& foreground , long windowStyle, bool ignoreBlack )
556 void wxWidgetIPhoneImpl::InstallEventHandler( WXWidget control )
558 WXWidget c = control ? control : (WXWidget) m_osxView;
559 wxWidgetImpl::Associate( c, this ) ;
561 if ([c isKindOfClass:[UIControl class] ])
563 UIControl* cc = (UIControl*) c;
565 [cc addTarget:self action:@selector(touchUpInsideAction:event:) forControlEvents:UIControlEventTouchUpInside];
570 void wxWidgetIPhoneImpl::DoNotifyFocusEvent(bool receivedFocus, wxWidgetImpl* otherWindow)
572 wxWindow* thisWindow = GetWXPeer();
573 if ( thisWindow->MacGetTopLevelWindow() && NeedsFocusRect() )
575 thisWindow->MacInvalidateBorders();
580 wxLogTrace(wxT("Focus"), wxT("focus set(%p)"), static_cast<void*>(thisWindow));
581 wxChildFocusEvent eventFocus((wxWindow*)thisWindow);
582 thisWindow->HandleWindowEvent(eventFocus);
585 if ( thisWindow->GetCaret() )
586 thisWindow->GetCaret()->OnSetFocus();
589 wxFocusEvent event(wxEVT_SET_FOCUS, thisWindow->GetId());
590 event.SetEventObject(thisWindow);
592 event.SetWindow(otherWindow->GetWXPeer());
593 thisWindow->HandleWindowEvent(event) ;
595 else // !receivedFocuss
598 if ( thisWindow->GetCaret() )
599 thisWindow->GetCaret()->OnKillFocus();
602 wxLogTrace(wxT("Focus"), wxT("focus lost(%p)"), static_cast<void*>(thisWindow));
604 wxFocusEvent event( wxEVT_KILL_FOCUS, thisWindow->GetId());
605 event.SetEventObject(thisWindow);
607 event.SetWindow(otherWindow->GetWXPeer());
608 thisWindow->HandleWindowEvent(event) ;
612 typedef void (*wxOSX_DrawRectHandlerPtr)(UIView* self, SEL _cmd, CGRect rect);
613 typedef BOOL (*wxOSX_FocusHandlerPtr)(UIView* self, SEL _cmd);
615 bool wxWidgetIPhoneImpl::becomeFirstResponder(WXWidget slf, void *_cmd)
617 wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
618 // get the current focus before running becomeFirstResponder
619 UIView* otherView = FindFocus();
620 wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
621 BOOL r = superimpl(slf, (SEL)_cmd);
624 DoNotifyFocusEvent( true, otherWindow );
629 bool wxWidgetIPhoneImpl::resignFirstResponder(WXWidget slf, void *_cmd)
631 wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
632 BOOL r = superimpl(slf, (SEL)_cmd);
633 // get the current focus after running resignFirstResponder
634 UIView* otherView = FindFocus();
635 wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
636 // NSTextViews have an editor as true responder, therefore the might get the
637 // resign notification if their editor takes over, don't trigger any event hen
638 if ( r && otherWindow != this)
640 DoNotifyFocusEvent( false, otherWindow );
645 void wxWidgetIPhoneImpl::drawRect(CGRect* rect, WXWidget slf, void *WXUNUSED(_cmd))
647 CGContextRef context = (CGContextRef) UIGraphicsGetCurrentContext();
648 CGContextSaveGState( context );
651 CGContextSetFillColorWithColor( context, GetWXPeer()->GetBackgroundColour().GetCGColor());
652 CGContextFillRect(context, *rect );
654 GetWXPeer()->MacSetCGContextRef( context );
656 GetWXPeer()->GetUpdateRegion() =
657 wxRegion(rect->origin.x,rect->origin.y,rect->size.width,rect->size.height) ;
659 wxRegion updateRgn( wxFromNSRect( slf, *rect ) );
661 wxWindow* wxpeer = GetWXPeer();
662 wxpeer->GetUpdateRegion() = updateRgn;
663 wxpeer->MacSetCGContextRef( context );
665 bool handled = wxpeer->MacDoRedraw( 0 );
667 CGContextRestoreGState( context );
669 CGContextSaveGState( context );
673 SEL _cmd = @selector(drawRect:);
674 wxOSX_DrawRectHandlerPtr superimpl = (wxOSX_DrawRectHandlerPtr) [[slf superclass] instanceMethodForSelector:_cmd];
675 if ( superimpl != wxOSX_drawRect )
677 superimpl(slf, _cmd, *rect);
678 CGContextRestoreGState( context );
679 CGContextSaveGState( context );
682 wxpeer->MacPaintChildrenBorders();
683 wxpeer->MacSetCGContextRef( NULL );
685 CGContextRestoreGState( context );
688 void wxWidgetIPhoneImpl::touchEvent(NSSet* touches, UIEvent *event, WXWidget slf, void *WXUNUSED(_cmd))
690 bool inRecursion = false;
694 UITouch *touch = [touches anyObject];
695 CGPoint clickLocation;
696 if ( [touch view] != slf && IsRootControl() )
698 NSLog(@"self is %@ and touch view is %@",slf,[touch view]);
704 clickLocation = [touch locationInView:slf];
705 wxPoint pt = wxFromNSPoint( m_osxView, clickLocation );
707 wxMouseEvent wxevent(wxEVT_LEFT_DOWN);
708 SetupMouseEvent( wxevent , touches, event ) ;
711 wxevent.SetEventObject( GetWXPeer() ) ;
712 //?wxevent.SetId( GetWXPeer()->GetId() ) ;
714 GetWXPeer()->HandleWindowEvent(wxevent);
718 void wxWidgetIPhoneImpl::touchUpInsideAction(void* sender, WX_UIEvent evt, WXWidget slf, void* _cmd)
726 wxWidgetImpl* wxWidgetImpl::CreateUserPane( wxWindowMac* wxpeer, wxWindowMac* WXUNUSED(parent),
727 wxWindowID WXUNUSED(id), const wxPoint& pos, const wxSize& size,
728 long WXUNUSED(style), long WXUNUSED(extraStyle))
730 UIView* sv = (wxpeer->GetParent()->GetHandle() );
732 CGRect r = CGRectMake( pos.x, pos.y, size.x, size.y) ;
733 // Rect bounds = wxMacGetBoundsForControl( wxpeer, pos , size ) ;
734 wxUIView* v = [[wxUIView alloc] initWithFrame:r];
735 sv.clipsToBounds = YES;
736 sv.contentMode = UIViewContentModeRedraw;
737 sv.clearsContextBeforeDrawing = NO;
738 wxWidgetIPhoneImpl* c = new wxWidgetIPhoneImpl( wxpeer, v );