Implement mouse entered, exited, and synthesize move events while the mouse is inside.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@46229 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
David Elliott
2007-05-28 04:22:10 +00:00
parent ae04c0f156
commit 7c5a378ff2
7 changed files with 263 additions and 4 deletions

View File

@@ -32,6 +32,7 @@ public:
virtual wxWindow* GetWxWindow() const
{ return NULL; }
virtual void Cocoa_FrameChanged(void) = 0;
virtual void Cocoa_synthesizeMouseMoved(void) = 0;
virtual bool Cocoa_acceptsFirstMouse(bool &acceptsFirstMouse, WX_NSEvent theEvent)
{ return false; }
virtual bool Cocoa_drawRect(const NSRect &rect)
@@ -62,6 +63,10 @@ public:
{ return false; }
virtual bool Cocoa_resetCursorRects()
{ return false; }
virtual bool Cocoa_viewDidMoveToWindow()
{ return false; }
virtual bool Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow)
{ return false; }
virtual ~wxCocoaNSView() { }
};

View File

@@ -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)

View File

@@ -0,0 +1,38 @@
/////////////////////////////////////////////////////////////////////////////
// Name: wx/cocoa/trackingrectmanager.h
// Purpose: wxCocoaTrackingRectManager
// Notes: Source in window.mm
// Author: David Elliott <dfe@cox.net>
// 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 <CoreFoundation/CFRunLoop.h>
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__

View File

@@ -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
// ------------------------------------------------------------------------
@@ -88,6 +90,7 @@ protected:
void InitMouseEvent(wxMouseEvent &event, WX_NSEvent cocoaEvent);
virtual wxWindow* GetWxWindow() const;
virtual void Cocoa_FrameChanged(void);
virtual void Cocoa_synthesizeMouseMoved(void);
virtual bool Cocoa_drawRect(const NSRect &rect);
virtual bool Cocoa_mouseDown(WX_NSEvent theEvent);
virtual bool Cocoa_mouseDragged(WX_NSEvent theEvent);
@@ -102,11 +105,14 @@ protected:
virtual bool Cocoa_otherMouseDragged(WX_NSEvent theEvent);
virtual bool Cocoa_otherMouseUp(WX_NSEvent theEvent);
virtual bool Cocoa_resetCursorRects();
virtual bool Cocoa_viewDidMoveToWindow();
virtual bool Cocoa_viewWillMoveToWindow(WX_NSWindow newWindow);
void SetNSView(WX_NSView cocoaNSView);
WX_NSView m_cocoaNSView;
wxWindowCocoaHider *m_cocoaHider;
wxWindowCocoaScrollView *m_wxCocoaScrollView;
bool m_isInPaint;
wxCocoaTrackingRectManager *m_visibleTrackingRectManager;
static wxWindow *sm_capturedWindow;
virtual void CocoaReplaceView(WX_NSView oldView, WX_NSView newView);
void SetInitialFrameRect(const wxPoint& pos, const wxSize& size);

View File

@@ -166,6 +166,20 @@ void wxCocoaNSView::DisassociateNSView(WX_NSView cocoaNSView)
[super resetCursorRects];
}
- (void)viewDidMoveToWindow
{
wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(self);
if( !win || !win->Cocoa_viewDidMoveToWindow() )
[super viewDidMoveToWindow];
}
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
{
wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(self);
if( !win || !win->Cocoa_viewWillMoveToWindow(newWindow) )
[super viewWillMoveToWindow:newWindow];
}
@end // implementation WXNSView
WX_IMPLEMENT_GET_OBJC_CLASS(WXNSView,NSView)
@@ -178,6 +192,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 +205,13 @@ WX_DECLARE_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject)
win->Cocoa_FrameChanged();
}
- (void)synthesizeMouseMovedForView: (NSView *)theView
{
wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(theView);
wxCHECK_RET(win,wxT("synthesizeMouseMovedForView received but no wxWindow exists"));
win->Cocoa_synthesizeMouseMoved();
}
@end // implementation wxNSViewNotificationObserver
WX_IMPLEMENT_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject)

View File

@@ -18,6 +18,7 @@
#endif
#include "wx/cocoa/autorelease.h"
#include "wx/cocoa/trackingrectmanager.h"
#include "wx/cocoa/objc/objc_uniquifying.h"
#import <AppKit/NSControl.h>
@@ -44,6 +45,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)
@@ -156,6 +159,20 @@ WX_DECLARE_GET_OBJC_CLASS(wxNonControlNSControl,NSControl)
[super resetCursorRects];
}
- (void)viewDidMoveToWindow
{
wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(self);
if( !win || !win->Cocoa_viewDidMoveToWindow() )
[super viewDidMoveToWindow];
}
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
{
wxCocoaNSView *win = wxCocoaNSView::GetFromCocoa(self);
if( !win || !win->Cocoa_viewWillMoveToWindow(newWindow) )
[super viewWillMoveToWindow:newWindow];
}
@end // wxNonControlNSControl
WX_IMPLEMENT_GET_OBJC_CLASS(wxNonControlNSControl,NSControl)
@@ -184,6 +201,9 @@ bool wxControl::Create(wxWindow *parent, wxWindowID winid,
m_parent->CocoaAddChild(this);
SetInitialFrameRect(pos,size);
// Controls should have a viewable-area tracking rect by default
m_visibleTrackingRectManager = new wxCocoaTrackingRectManager(this);
return true;
}

View File

@@ -22,7 +22,9 @@
#include "wx/cocoa/autorelease.h"
#include "wx/cocoa/string.h"
#include "wx/cocoa/trackingrectmanager.h"
#import <Foundation/NSRunLoop.h>
#include "wx/cocoa/objc/NSView.h"
#import <AppKit/NSEvent.h>
#import <AppKit/NSScrollView.h>
@@ -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();
};
@@ -317,6 +321,7 @@ void wxWindowCocoa::Init()
m_wxCocoaScrollView = NULL;
m_isBeingDeleted = false;
m_isInPaint = false;
m_visibleTrackingRectManager = NULL;
}
// Constructor
@@ -377,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);
@@ -495,17 +504,64 @@ 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)
{
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)
{
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;
}
@@ -576,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);
@@ -584,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;
@@ -592,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
@@ -1036,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<wxCocoaNSView*>(info);
win->Cocoa_synthesizeMouseMoved();
}
}
void wxCocoaTrackingRectManager::BeginSynthesizingEvents()
{
CFRunLoopObserverContext observerContext =
{ 0
, static_cast<wxCocoaNSView*>(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]);
}