X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ca5db7b21310333026314ff9d21c2a5168546177..999020e1bb848e4f5b34a129018dae5076708a6c:/src/cocoa/window.mm diff --git a/src/cocoa/window.mm b/src/cocoa/window.mm index 74ea906beb..83874d9ad0 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 #import @@ -110,6 +112,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 @@ -135,6 +138,7 @@ protected: wxWindowCocoa *m_owner; WX_NSScrollView m_cocoaNSScrollView; virtual void Cocoa_FrameChanged(void); + virtual void Cocoa_synthesizeMouseMoved(void) {} private: wxWindowCocoaScrollView(); }; @@ -145,6 +149,7 @@ private: @interface wxDummyNSView : NSView - (NSView *)hitTest:(NSPoint)aPoint; @end +WX_DECLARE_GET_OBJC_CLASS(wxDummyNSView,NSView) @implementation wxDummyNSView : NSView - (NSView *)hitTest:(NSPoint)aPoint @@ -153,6 +158,7 @@ private: } @end +WX_IMPLEMENT_GET_OBJC_CLASS(wxDummyNSView,NSView) // ======================================================================== // wxWindowCocoaHider @@ -162,7 +168,7 @@ wxWindowCocoaHider::wxWindowCocoaHider(wxWindow *owner) { wxASSERT(owner); wxASSERT(owner->GetNSViewForHiding()); - m_dummyNSView = [[wxDummyNSView alloc] + m_dummyNSView = [[WX_GET_OBJC_CLASS(wxDummyNSView) alloc] initWithFrame:[owner->GetNSViewForHiding() frame]]; [m_dummyNSView setAutoresizingMask: [owner->GetNSViewForHiding() autoresizingMask]]; AssociateNSView(m_dummyNSView); @@ -199,6 +205,7 @@ bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect) @interface wxFlippedNSClipView : NSClipView - (BOOL)isFlipped; @end +WX_DECLARE_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView) @implementation wxFlippedNSClipView : NSClipView - (BOOL)isFlipped @@ -207,6 +214,7 @@ bool wxWindowCocoaHider::Cocoa_drawRect(const NSRect& rect) } @end +WX_IMPLEMENT_GET_OBJC_CLASS(wxFlippedNSClipView,NSClipView) // ======================================================================== // wxWindowCocoaScrollView @@ -223,7 +231,7 @@ wxWindowCocoaScrollView::wxWindowCocoaScrollView(wxWindow *owner) /* Replace the default NSClipView with a flipped one. This ensures scrolling is "pinned" to the top-left instead of bottom-right. */ - NSClipView *flippedClip = [[wxFlippedNSClipView alloc] + NSClipView *flippedClip = [[WX_GET_OBJC_CLASS(wxFlippedNSClipView) alloc] initWithFrame: [[m_cocoaNSScrollView contentView] frame]]; [m_cocoaNSScrollView setContentView:flippedClip]; [flippedClip release]; @@ -313,7 +321,7 @@ void wxWindowCocoa::Init() m_wxCocoaScrollView = NULL; m_isBeingDeleted = false; m_isInPaint = false; - m_shouldBeEnabled = true; + m_visibleTrackingRectManager = NULL; } // Constructor @@ -328,7 +336,7 @@ bool wxWindow::Create(wxWindow *parent, wxWindowID winid, // TODO: create the window m_cocoaNSView = NULL; - SetNSView([[NSView alloc] initWithFrame: MakeDefaultNSRect(size)]); + SetNSView([[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: MakeDefaultNSRect(size)]); [m_cocoaNSView release]; if (m_parent) @@ -374,6 +382,10 @@ void wxWindowCocoa::CocoaRemoveFromParent(void) void wxWindowCocoa::SetNSView(WX_NSView cocoaNSView) { + // Clear the visible area tracking rect if we have one. + delete m_visibleTrackingRectManager; + m_visibleTrackingRectManager = NULL; + 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); @@ -492,18 +504,65 @@ 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(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 + return false; } bool wxWindowCocoa::Cocoa_mouseExited(WX_NSEvent theEvent) { - return false; + 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 + return false; } bool wxWindowCocoa::Cocoa_mouseDown(WX_NSEvent theEvent) @@ -573,7 +632,9 @@ 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(m_visibleTrackingRectManager != NULL) + m_visibleTrackingRectManager->RebuildTrackingRect(); wxSizeEvent event(GetSize(), m_windowId); event.SetEventObject(this); GetEventHandler()->ProcessEvent(event); @@ -581,6 +642,10 @@ void wxWindowCocoa::Cocoa_FrameChanged(void) bool wxWindowCocoa::Cocoa_resetCursorRects() { + wxLogTrace(wxTRACE_COCOA,wxT("wxWindow=%p::Cocoa_resetCursorRects"),this); + if(m_visibleTrackingRectManager != NULL) + m_visibleTrackingRectManager->RebuildTrackingRect(); + if(!m_cursor.GetNSCursor()) return false; @@ -589,6 +654,24 @@ bool wxWindowCocoa::Cocoa_resetCursorRects() return true; } +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; +} + bool wxWindow::Close(bool force) { // The only reason this function exists is that it is virtual and @@ -601,32 +684,9 @@ void wxWindow::CocoaReplaceView(WX_NSView oldView, WX_NSView newView) [[oldView superview] replaceSubview:oldView with:newView]; } -bool wxWindow::EnableSelfAndChildren(bool enable) -{ - // If the state isn't changing, don't do anything - if(!wxWindowBase::Enable(enable && m_shouldBeEnabled)) - return false; - // Set the state of the Cocoa window - CocoaSetEnabled(m_isEnabled); - // Disable all children or (if enabling) return them to their proper state - for(wxWindowList::compatibility_iterator node = GetChildren().GetFirst(); - node; node = node->GetNext()) - { - node->GetData()->EnableSelfAndChildren(enable); - } - return true; -} - -bool wxWindow::Enable(bool enable) +void wxWindow::DoEnable(bool enable) { - // Keep track of what the window SHOULD be doing - m_shouldBeEnabled = enable; - // If the parent is disabled for any reason, then this window will be too. - if(!IsTopLevel() && GetParent()) - { - enable = enable && GetParent()->IsEnabled(); - } - return EnableSelfAndChildren(enable); + CocoaSetEnabled(enable); } bool wxWindow::Show(bool show) @@ -857,12 +917,13 @@ void wxWindow::CocoaSetWxWindowSize(int width, int height) void wxWindow::SetLabel(const wxString& WXUNUSED(label)) { - // TODO + // Intentional no-op. } wxString wxWindow::GetLabel() const { - // TODO + // General Get/Set of labels is implemented in wxControlBase + wxLogDebug(wxT("wxWindow::GetLabel: Should be overridden if needed.")); return wxEmptyString; } @@ -1055,3 +1116,89 @@ 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) +{ + NSPoint screenMouseLocation = [NSEvent mouseLocation]; + if(screenMouseLocation.x != s_lastScreenMouseLocation.x || screenMouseLocation.y != s_lastScreenMouseLocation.y) + { + wxCocoaNSView *win = reinterpret_cast(info); + win->Cocoa_synthesizeMouseMoved(); + } +} + +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]); +} +