diff --git a/include/wx/cocoa/NSView.h b/include/wx/cocoa/NSView.h index 643f85886d..1d98ef7a7c 100644 --- a/include/wx/cocoa/NSView.h +++ b/include/wx/cocoa/NSView.h @@ -37,6 +37,9 @@ public: virtual wxWindow* GetWxWindow() const { return NULL; } virtual void Cocoa_FrameChanged(void) = 0; +#if 0 // ABI incompatibility + virtual void Cocoa_synthesizeMouseMoved(void) = 0; +#endif virtual bool Cocoa_acceptsFirstMouse(bool &acceptsFirstMouse, WX_NSEvent theEvent) { return false; } virtual bool Cocoa_drawRect(const NSRect &rect) @@ -67,6 +70,12 @@ public: { return false; } virtual bool Cocoa_resetCursorRects() { return false; } +#if 0 // ABI incompatibility + virtual bool Cocoa_viewDidMoveToWindow() + { return false; } + virtual bool Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow) + { return false; } +#endif virtual ~wxCocoaNSView() { } }; diff --git a/include/wx/cocoa/objc/NSView.h b/include/wx/cocoa/objc/NSView.h index 2f2ab86ff7..885ed39b6c 100644 --- a/include/wx/cocoa/objc/NSView.h +++ b/include/wx/cocoa/objc/NSView.h @@ -37,6 +37,8 @@ - (void)otherMouseDragged:(NSEvent *)theEvent; - (void)otherMouseUp:(NSEvent *)theEvent; - (void)resetCursorRects; +- (void)viewDidMoveToWindow; +- (void)viewWillMoveToWindow:(NSWindow *)newWindow; @end // WXNSView WX_DECLARE_GET_OBJC_CLASS(WXNSView,NSView) diff --git a/include/wx/cocoa/trackingrectmanager.h b/include/wx/cocoa/trackingrectmanager.h new file mode 100644 index 0000000000..3712b0d87a --- /dev/null +++ b/include/wx/cocoa/trackingrectmanager.h @@ -0,0 +1,38 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/cocoa/trackingrectmanager.h +// Purpose: wxCocoaTrackingRectManager +// Notes: Source in window.mm +// Author: David Elliott +// Modified by: +// Created: 2007/05/02 +// RCS-ID: $Id$ +// Copyright: (c) 2007 Software 2000 Ltd. +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// +#ifndef __WX_COCOA_TRACKINGRECTMANAGER_H__ +#define __WX_COCOA_TRACKINGRECTMANAGER_H__ + +#include + +class wxCocoaTrackingRectManager +{ + DECLARE_NO_COPY_CLASS(wxCocoaTrackingRectManager) +public: + wxCocoaTrackingRectManager(wxWindow *window); + void ClearTrackingRect(); + void BuildTrackingRect(); + void RebuildTrackingRect(); + bool IsOwnerOfEvent(NSEvent *anEvent); + ~wxCocoaTrackingRectManager(); + void BeginSynthesizingEvents(); + void StopSynthesizingEvents(); +protected: + wxWindow *m_window; + bool m_isTrackingRectActive; + int m_trackingRectTag; + CFRunLoopObserverRef m_runLoopObserver; +private: + wxCocoaTrackingRectManager(); +}; + +#endif // ndef __WX_COCOA_TRACKINGRECTMANAGER_H__ diff --git a/include/wx/cocoa/window.h b/include/wx/cocoa/window.h index 33e5df7836..3209714302 100644 --- a/include/wx/cocoa/window.h +++ b/include/wx/cocoa/window.h @@ -22,6 +22,7 @@ DECLARE_WXCOCOA_OBJC_CLASS(NSAffineTransform); class wxWindowCocoaHider; class wxWindowCocoaScrollView; +class wxCocoaTrackingRectManager; // ======================================================================== // wxWindowCocoa @@ -33,6 +34,7 @@ class WXDLLEXPORT wxWindowCocoa: public wxWindowBase, protected wxCocoaNSView DECLARE_EVENT_TABLE() friend wxWindow *wxWindowBase::GetCapture(); friend class wxWindowCocoaScrollView; + friend class wxCocoaTrackingRectManager; // ------------------------------------------------------------------------ // initialization // ------------------------------------------------------------------------ @@ -93,6 +95,11 @@ protected: void InitMouseEvent(wxMouseEvent &event, WX_NSEvent cocoaEvent); virtual wxWindow* GetWxWindow() const; virtual void Cocoa_FrameChanged(void); +#if 0 // ABI incompatibility + virtual void Cocoa_synthesizeMouseMoved(void); +#else + void Cocoa_synthesizeMouseMoved(void); +#endif virtual bool Cocoa_drawRect(const NSRect &rect); virtual bool Cocoa_mouseDown(WX_NSEvent theEvent); virtual bool Cocoa_mouseDragged(WX_NSEvent theEvent); @@ -107,11 +114,18 @@ protected: virtual bool Cocoa_otherMouseDragged(WX_NSEvent theEvent); virtual bool Cocoa_otherMouseUp(WX_NSEvent theEvent); virtual bool Cocoa_resetCursorRects(); +#if 0 // ABI incompatibility + virtual bool Cocoa_viewDidMoveToWindow(); + virtual bool Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow); +#endif void SetNSView(WX_NSView cocoaNSView); WX_NSView m_cocoaNSView; wxWindowCocoaHider *m_cocoaHider; wxWindowCocoaScrollView *m_wxCocoaScrollView; bool m_isInPaint; +#if 0 // ABI incompatibility + wxCocoaTrackingRectManager *m_visibleTrackingRectManager; +#endif static wxWindow *sm_capturedWindow; virtual void CocoaReplaceView(WX_NSView oldView, WX_NSView newView); void SetInitialFrameRect(const wxPoint& pos, const wxSize& size); diff --git a/src/cocoa/NSView.mm b/src/cocoa/NSView.mm index b794f26949..00fadb8da6 100644 --- a/src/cocoa/NSView.mm +++ b/src/cocoa/NSView.mm @@ -166,6 +166,24 @@ void wxCocoaNSView::DisassociateNSView(WX_NSView cocoaNSView) [super resetCursorRects]; } +- (void)viewDidMoveToWindow +{ +#if 0 // ABI incompatibility + wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(self); + if( !win || !win->Cocoa_viewDidMoveToWindow() ) +#endif + [super viewDidMoveToWindow]; +} + +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ +#if 0 // ABI incompatibility + wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(self); + if( !win || !win->Cocoa_viewWillMoveToWindow(newWindow) ) +#endif + [super viewWillMoveToWindow:newWindow]; +} + @end // implementation WXNSView WX_IMPLEMENT_GET_OBJC_CLASS(WXNSView,NSView) @@ -178,6 +196,7 @@ WX_IMPLEMENT_GET_OBJC_CLASS(WXNSView,NSView) } - (void)notificationFrameChanged: (NSNotification *)notification; +- (void)synthesizeMouseMovedForView: (NSView *)theView; @end // interface wxNSViewNotificationObserver WX_DECLARE_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject) @@ -190,6 +209,15 @@ WX_DECLARE_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject) win->Cocoa_FrameChanged(); } +- (void)synthesizeMouseMovedForView: (NSView *)theView +{ +#if 0 // ABI incompatibility + wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(theView); + wxCHECK_RET(win,wxT("synthesizeMouseMovedForView received but no wxWindow exists")); + win->Cocoa_synthesizeMouseMoved(); +#endif +} + @end // implementation wxNSViewNotificationObserver WX_IMPLEMENT_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject) diff --git a/src/cocoa/control.mm b/src/cocoa/control.mm index 2392fb3a7a..088ff587ec 100644 --- a/src/cocoa/control.mm +++ b/src/cocoa/control.mm @@ -19,6 +19,7 @@ #include "wx/cocoa/string.h" #include "wx/cocoa/autorelease.h" +#include "wx/cocoa/trackingrectmanager.h" #include "wx/cocoa/objc/objc_uniquifying.h" #import @@ -46,6 +47,8 @@ - (void)otherMouseDragged:(NSEvent *)theEvent; - (void)otherMouseUp:(NSEvent *)theEvent; - (void)resetCursorRects; +- (void)viewDidMoveToWindow; +- (void)viewWillMoveToWindow:(NSWindow *)newWindow; @end // wxNonControlNSControl WX_DECLARE_GET_OBJC_CLASS(wxNonControlNSControl,NSControl) @@ -171,6 +174,24 @@ WX_DECLARE_GET_OBJC_CLASS(wxNonControlNSControl,NSControl) [super resetCursorRects]; } +- (void)viewDidMoveToWindow +{ +#if 0 // ABI incompatibility + wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(self); + if( !win || !win->Cocoa_viewDidMoveToWindow() ) +#endif + [super viewDidMoveToWindow]; +} + +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ +#if 0 // ABI incompatibility + wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(self); + if( !win || !win->Cocoa_viewWillMoveToWindow(newWindow) ) +#endif + [super viewWillMoveToWindow:newWindow]; +} + @end // wxNonControlNSControl WX_IMPLEMENT_GET_OBJC_CLASS(wxNonControlNSControl,NSControl) @@ -199,6 +220,11 @@ bool wxControl::Create(wxWindow *parent, wxWindowID winid, m_parent->CocoaAddChild(this); SetInitialFrameRect(pos,size); +#if 0 // ABI incompatibility + // Controls should have a viewable-area tracking rect by default + m_visibleTrackingRectManager = new wxCocoaTrackingRectManager(this); +#endif + return true; } diff --git a/src/cocoa/window.mm b/src/cocoa/window.mm index 633500a25a..525a430cc0 100644 --- a/src/cocoa/window.mm +++ b/src/cocoa/window.mm @@ -22,8 +22,10 @@ #include "wx/cocoa/autorelease.h" #include "wx/cocoa/string.h" +#include "wx/cocoa/trackingrectmanager.h" #import +#import #include "wx/cocoa/objc/NSView.h" #import #import @@ -123,6 +125,7 @@ protected: wxWindowCocoa *m_owner; WX_NSView m_dummyNSView; virtual void Cocoa_FrameChanged(void); +// virtual void Cocoa_synthesizeMouseMoved(void) {} #ifdef WXCOCOA_FILL_DUMMY_VIEW virtual bool Cocoa_drawRect(const NSRect& rect); #endif //def WXCOCOA_FILL_DUMMY_VIEW @@ -148,6 +151,7 @@ protected: wxWindowCocoa *m_owner; WX_NSScrollView m_cocoaNSScrollView; virtual void Cocoa_FrameChanged(void); +// virtual void Cocoa_synthesizeMouseMoved(void) {} private: wxWindowCocoaScrollView(); }; @@ -331,6 +335,9 @@ void wxWindowCocoa::Init() m_isBeingDeleted = false; m_isInPaint = false; m_shouldBeEnabled = true; +#if 0 // ABI incompatibility + m_visibleTrackingRectManager = NULL; +#endif } // Constructor @@ -391,6 +398,12 @@ void wxWindowCocoa::CocoaRemoveFromParent(void) void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView) { +#if 0 // ABI incompatibility + // Clear the visible area tracking rect if we have one. + delete m_visibleTrackingRectManager; + m_visibleTrackingRectManager = NULL; +#endif + bool need_debug = cocoaNSView || m_cocoaNSView; if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxWindowCocoa=%p::SetNSView [m_cocoaNSView=%p retainCount]=%d"),this,m_cocoaNSView,[m_cocoaNSView retainCount]); DisassociateNSView(m_cocoaNSView); @@ -509,18 +522,69 @@ bool wxWindowCocoa::Cocoa_mouseMoved(WX_NSEvent theEvent) { wxMouseEvent event(wxEVT_MOTION); InitMouseEvent(event,theEvent); - wxLogTrace(wxTRACE_COCOA,wxT("Mouse Drag @%d,%d"),event.m_x,event.m_y); + wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_mouseMoved @%d,%d"),this,event.m_x,event.m_y); return GetEventHandler()->ProcessEvent(event); } +void wxWindowCocoa::Cocoa_synthesizeMouseMoved() +{ + wxMouseEvent event(wxEVT_MOTION); + NSWindow *window = [GetNSView() window]; + NSPoint locationInWindow = [window mouseLocationOutsideOfEventStream]; + NSPoint cocoaPoint = [m_cocoaNSView convertPoint:locationInWindow fromView:nil]; + + NSPoint pointWx = CocoaTransformBoundsToWx(cocoaPoint); + // FIXME: Should we be adjusting for client area origin? + const wxPoint &clientorigin = GetClientAreaOrigin(); + event.m_x = (wxCoord)pointWx.x - clientorigin.x; + event.m_y = (wxCoord)pointWx.y - clientorigin.y; + + // TODO: Handle shift, control, alt, meta flags + event.SetEventObject(this); + event.SetId(GetId()); + + wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Synthesized Mouse Moved @%d,%d"),this,event.m_x,event.m_y); + GetEventHandler()->ProcessEvent(event); +} + bool wxWindowCocoa::Cocoa_mouseEntered(WX_NSEvent theEvent) { - return false; +#if 0 // ABI incompatibility + if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent)) + { + m_visibleTrackingRectManager->BeginSynthesizingEvents(); + + // Although we synthesize the mouse moved events we don't poll for them but rather send them only when + // some other event comes in. That other event is (guess what) mouse moved events that will be sent + // to the NSWindow which will forward them on to the first responder. We are not likely to be the + // first responder, so the mouseMoved: events are effectively discarded. + [[GetNSView() window] setAcceptsMouseMovedEvents:YES]; + + wxMouseEvent event(wxEVT_ENTER_WINDOW); + InitMouseEvent(event,theEvent); + wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Mouse Entered @%d,%d"),this,event.m_x,event.m_y); + return GetEventHandler()->ProcessEvent(event); + } + else +#endif + return false; } bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent) { - return false; +#if 0 // ABI incompatibility + if(m_visibleTrackingRectManager != NULL && m_visibleTrackingRectManager->IsOwnerOfEvent(theEvent)) + { + m_visibleTrackingRectManager->StopSynthesizingEvents(); + + wxMouseEvent event(wxEVT_LEAVE_WINDOW); + InitMouseEvent(event,theEvent); + wxLogTrace(wxTRACE_COCOA,wxT("wxwin=%p Mouse Exited @%d,%d"),this,event.m_x,event.m_y); + return GetEventHandler()->ProcessEvent(event); + } + else +#endif + return false; } bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent) @@ -590,7 +654,11 @@ bool wxWindowCocoa::Cocoa_otherMouseUp(WX_NSEvent theEvent) void wxWindowCocoa::Cocoa_FrameChanged(void) { - wxLogTrace(wxTRACE_COCOA,wxT("Cocoa_FrameChanged")); + wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_FrameChanged"),this); +#if 0 // ABI incompatibility + if(m_visibleTrackingRectManager != NULL) + m_visibleTrackingRectManager->RebuildTrackingRect(); +#endif wxSizeEvent event(GetSize(), m_windowId); event.SetEventObject(this); GetEventHandler()->ProcessEvent(event); @@ -607,6 +675,12 @@ bool wxWindowCocoa::SetCursor(const wxCursor &cursor) bool wxWindowCocoa::Cocoa_resetCursorRects() { + wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this); +#if 0 // ABI incompatibility + if(m_visibleTrackingRectManager != NULL) + m_visibleTrackingRectManager->RebuildTrackingRect(); +#endif + if(!m_cursor.GetNSCursor()) return false; @@ -615,6 +689,26 @@ bool wxWindowCocoa::Cocoa_resetCursorRects() return true; } +#if 0 // ABI incompatibility +bool wxWindowCocoa::Cocoa_viewDidMoveToWindow() +{ + wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewDidMoveToWindow"),this); + // Set up new tracking rects. I am reasonably sure the new window must be set before doing this. + if(m_visibleTrackingRectManager != NULL) + m_visibleTrackingRectManager->BuildTrackingRect(); + return false; +} + +bool wxWindowCocoa::Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow) +{ + wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::viewWillMoveToWindow:%p"),this, newWindow); + // Clear tracking rects. It is imperative this be done before the new window is set. + if(m_visibleTrackingRectManager != NULL) + m_visibleTrackingRectManager->ClearTrackingRect(); + return false; +} +#endif + bool wxWindow::Close(bool force) { // The only reason this function exists is that it is virtual and @@ -1163,3 +1257,91 @@ wxWindow* wxFindWindowAtPointer(wxPoint& pt) pt = wxGetMousePosition(); return NULL; } + + +// ======================================================================== +// wxCocoaTrackingRectManager +// ======================================================================== + +wxCocoaTrackingRectManager::wxCocoaTrackingRectManager(wxWindow *window) +: m_window(window) +{ + m_isTrackingRectActive = false; + m_runLoopObserver = NULL; + BuildTrackingRect(); +} + +void wxCocoaTrackingRectManager::ClearTrackingRect() +{ + if(m_isTrackingRectActive) + { + [m_window->GetNSView() removeTrackingRect:m_trackingRectTag]; + m_isTrackingRectActive = false; + } + // If we were doing periodic events we need to clear those too + StopSynthesizingEvents(); +} + +void wxCocoaTrackingRectManager::StopSynthesizingEvents() +{ + if(m_runLoopObserver != NULL) + { + CFRunLoopRemoveObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes); + CFRelease(m_runLoopObserver); + m_runLoopObserver = NULL; + } +} + +void wxCocoaTrackingRectManager::BuildTrackingRect() +{ + wxASSERT_MSG(!m_isTrackingRectActive, wxT("Tracking rect was not cleared")); + if([m_window->GetNSView() window] != nil) + { + m_trackingRectTag = [m_window->GetNSView() addTrackingRect:[m_window->GetNSView() visibleRect] owner:m_window->GetNSView() userData:NULL assumeInside:NO]; + m_isTrackingRectActive = true; + } +} + +static NSPoint s_lastScreenMouseLocation = NSZeroPoint; + +static void SynthesizeMouseMovedEvent(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) +{ +#if 0 // ABI incompatibility + NSPoint screenMouseLocation = [NSEvent mouseLocation]; + if(screenMouseLocation.x != s_lastScreenMouseLocation.x || screenMouseLocation.y != s_lastScreenMouseLocation.y) + { + wxCocoaNSView *win = reinterpret_cast(info); + win->Cocoa_synthesizeMouseMoved(); + } +#endif +} + +void wxCocoaTrackingRectManager::BeginSynthesizingEvents() +{ + CFRunLoopObserverContext observerContext = + { 0 + , static_cast(m_window) + , NULL + , NULL + , NULL + }; + m_runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, TRUE, 0, SynthesizeMouseMovedEvent, &observerContext); + CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], m_runLoopObserver, kCFRunLoopCommonModes); +} + +void wxCocoaTrackingRectManager::RebuildTrackingRect() +{ + ClearTrackingRect(); + BuildTrackingRect(); +} + +wxCocoaTrackingRectManager::~wxCocoaTrackingRectManager() +{ + ClearTrackingRect(); +} + +bool wxCocoaTrackingRectManager::IsOwnerOfEvent(NSEvent *anEvent) +{ + return m_isTrackingRectActive && (m_trackingRectTag == [anEvent trackingNumber]); +} +