Improve native keyDown handling in wxOSX
Provide API for dealing with m_lastKeyDownEvent instead of using it directly and extend it to avoid sending duplicate events for keys which are mapped to multiple selectors, such as Ctrl-O with the default key bindings. Closes https://github.com/wxWidgets/wxWidgets/pull/1928
This commit is contained in:
committed by
Vadim Zeitlin
parent
de56f99c5a
commit
9be2c3717d
@@ -48,6 +48,26 @@ WXWindow WXDLLIMPEXP_CORE wxOSXGetKeyWindow();
|
|||||||
|
|
||||||
class WXDLLIMPEXP_FWD_CORE wxDialog;
|
class WXDLLIMPEXP_FWD_CORE wxDialog;
|
||||||
|
|
||||||
|
class WXDLLIMPEXP_FWD_CORE wxWidgetCocoaImpl;
|
||||||
|
|
||||||
|
// a class which disables sending wx keydown events useful when adding text programmatically, for wx-internal use only
|
||||||
|
class wxWidgetCocoaNativeKeyDownSuspender
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// stops sending keydown events for text inserted into this widget
|
||||||
|
explicit wxWidgetCocoaNativeKeyDownSuspender(wxWidgetCocoaImpl *target);
|
||||||
|
|
||||||
|
// resumes sending keydown events
|
||||||
|
~wxWidgetCocoaNativeKeyDownSuspender();
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxWidgetCocoaImpl *m_target;
|
||||||
|
NSEvent* m_nsevent;
|
||||||
|
bool m_wxsent;
|
||||||
|
|
||||||
|
wxDECLARE_NO_COPY_CLASS(wxWidgetCocoaNativeKeyDownSuspender);
|
||||||
|
};
|
||||||
|
|
||||||
class WXDLLIMPEXP_CORE wxWidgetCocoaImpl : public wxWidgetImpl
|
class WXDLLIMPEXP_CORE wxWidgetCocoaImpl : public wxWidgetImpl
|
||||||
{
|
{
|
||||||
public :
|
public :
|
||||||
@@ -203,7 +223,24 @@ public :
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
WXWidget m_osxView;
|
WXWidget m_osxView;
|
||||||
|
|
||||||
|
// begins processing of native key down event, storing the native event for later wx event generation
|
||||||
|
void BeginNativeKeyDownEvent( NSEvent* event );
|
||||||
|
// done with the current native key down event
|
||||||
|
void EndNativeKeyDownEvent();
|
||||||
|
// allow executing text changes without triggering key down events
|
||||||
|
|
||||||
|
// is currently processing a native key down event
|
||||||
|
bool IsInNativeKeyDown() const;
|
||||||
|
// the native key event
|
||||||
|
NSEvent* GetLastNativeKeyDownEvent();
|
||||||
|
// did send the wx event for the current native key down event
|
||||||
|
void SetKeyDownSent();
|
||||||
|
// was the wx event for the current native key down event sent
|
||||||
|
bool WasKeyDownSent() const;
|
||||||
|
|
||||||
NSEvent* m_lastKeyDownEvent;
|
NSEvent* m_lastKeyDownEvent;
|
||||||
|
bool m_lastKeyDownWXSent;
|
||||||
#if !wxOSX_USE_NATIVE_FLIPPED
|
#if !wxOSX_USE_NATIVE_FLIPPED
|
||||||
bool m_isFlipped;
|
bool m_isFlipped;
|
||||||
#endif
|
#endif
|
||||||
@@ -211,6 +248,8 @@ protected:
|
|||||||
// events, don't resend them
|
// events, don't resend them
|
||||||
bool m_hasEditor;
|
bool m_hasEditor;
|
||||||
|
|
||||||
|
friend class wxWidgetCocoaNativeKeyDownSuspender;
|
||||||
|
|
||||||
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxWidgetCocoaImpl);
|
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxWidgetCocoaImpl);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -534,4 +573,3 @@ private:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
// _WX_PRIVATE_COCOA_H_
|
// _WX_PRIVATE_COCOA_H_
|
||||||
|
|
||||||
|
@@ -781,7 +781,7 @@ bool wxNSTextViewControl::CanFocus() const
|
|||||||
void wxNSTextViewControl::insertText(NSString* str, WXWidget slf, void *_cmd)
|
void wxNSTextViewControl::insertText(NSString* str, WXWidget slf, void *_cmd)
|
||||||
{
|
{
|
||||||
NSString *text = [str isKindOfClass:[NSAttributedString class]] ? [(id)str string] : str;
|
NSString *text = [str isKindOfClass:[NSAttributedString class]] ? [(id)str string] : str;
|
||||||
if ( m_lastKeyDownEvent ==NULL || !DoHandleCharEvent(m_lastKeyDownEvent, text) )
|
if ( !IsInNativeKeyDown() || !DoHandleCharEvent(GetLastNativeKeyDownEvent(), text) )
|
||||||
{
|
{
|
||||||
wxOSX_TextEventHandlerPtr superimpl = (wxOSX_TextEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
|
wxOSX_TextEventHandlerPtr superimpl = (wxOSX_TextEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
|
||||||
superimpl(slf, (SEL)_cmd, str);
|
superimpl(slf, (SEL)_cmd, str);
|
||||||
@@ -988,10 +988,8 @@ void wxNSTextViewControl::WriteText(const wxString& str)
|
|||||||
{
|
{
|
||||||
wxString st(wxMacConvertNewlines10To13(str));
|
wxString st(wxMacConvertNewlines10To13(str));
|
||||||
wxMacEditHelper helper(m_textView);
|
wxMacEditHelper helper(m_textView);
|
||||||
NSEvent* formerEvent = m_lastKeyDownEvent;
|
wxWidgetCocoaNativeKeyDownSuspender suspend(this);
|
||||||
m_lastKeyDownEvent = nil;
|
|
||||||
[m_textView insertText:wxCFStringRef( st , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
|
[m_textView insertText:wxCFStringRef( st , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
|
||||||
m_lastKeyDownEvent = formerEvent;
|
|
||||||
// Some text styles have to be updated manually.
|
// Some text styles have to be updated manually.
|
||||||
DoUpdateTextStyle();
|
DoUpdateTextStyle();
|
||||||
}
|
}
|
||||||
@@ -1467,8 +1465,7 @@ void wxNSTextFieldControl::ShowPosition(long pos)
|
|||||||
|
|
||||||
void wxNSTextFieldControl::WriteText(const wxString& str)
|
void wxNSTextFieldControl::WriteText(const wxString& str)
|
||||||
{
|
{
|
||||||
NSEvent* formerEvent = m_lastKeyDownEvent;
|
wxWidgetCocoaNativeKeyDownSuspender suspend(this);
|
||||||
m_lastKeyDownEvent = nil;
|
|
||||||
NSText* editor = [m_textField currentEditor];
|
NSText* editor = [m_textField currentEditor];
|
||||||
if ( editor )
|
if ( editor )
|
||||||
{
|
{
|
||||||
@@ -1490,7 +1487,6 @@ void wxNSTextFieldControl::WriteText(const wxString& str)
|
|||||||
SetStringValue( val ) ;
|
SetStringValue( val ) ;
|
||||||
SetSelection( start + str.length() , start + str.length() ) ;
|
SetSelection( start + str.length() , start + str.length() ) ;
|
||||||
}
|
}
|
||||||
m_lastKeyDownEvent = formerEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxNSTextFieldControl::controlAction(WXWidget WXUNUSED(slf),
|
void wxNSTextFieldControl::controlAction(WXWidget WXUNUSED(slf),
|
||||||
|
@@ -1557,7 +1557,7 @@ void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lastKeyDownEvent = event;
|
BeginNativeKeyDownEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( GetFocusedViewInWindow([slf window]) != slf || m_hasEditor || !DoHandleKeyEvent(event) )
|
if ( GetFocusedViewInWindow([slf window]) != slf || m_hasEditor || !DoHandleKeyEvent(event) )
|
||||||
@@ -1565,7 +1565,11 @@ void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
|
|||||||
wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
|
wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
|
||||||
superimpl(slf, (SEL)_cmd, event);
|
superimpl(slf, (SEL)_cmd, event);
|
||||||
}
|
}
|
||||||
m_lastKeyDownEvent = NULL;
|
|
||||||
|
if ( [event type] == NSKeyDown )
|
||||||
|
{
|
||||||
|
EndNativeKeyDownEvent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class containing data used for gestures support.
|
// Class containing data used for gestures support.
|
||||||
@@ -2138,18 +2142,18 @@ void wxWidgetCocoaImpl::insertText(NSString* text, WXWidget slf, void *_cmd)
|
|||||||
bool result = false;
|
bool result = false;
|
||||||
if ( HasUserKeyHandling() && !m_hasEditor && [text length] > 0)
|
if ( HasUserKeyHandling() && !m_hasEditor && [text length] > 0)
|
||||||
{
|
{
|
||||||
if ( m_lastKeyDownEvent!=NULL && [text isEqualToString:[m_lastKeyDownEvent characters]])
|
if ( IsInNativeKeyDown() && [text isEqualToString:[GetLastNativeKeyDownEvent() characters]])
|
||||||
{
|
{
|
||||||
// If we have a corresponding key event, send wxEVT_KEY_DOWN now.
|
// If we have a corresponding key event, send wxEVT_KEY_DOWN now.
|
||||||
// (see also: wxWidgetCocoaImpl::DoHandleKeyEvent)
|
// (see also: wxWidgetCocoaImpl::DoHandleKeyEvent)
|
||||||
{
|
{
|
||||||
wxKeyEvent wxevent(wxEVT_KEY_DOWN);
|
wxKeyEvent wxevent(wxEVT_KEY_DOWN);
|
||||||
SetupKeyEvent( wxevent, m_lastKeyDownEvent );
|
SetupKeyEvent( wxevent, GetLastNativeKeyDownEvent() );
|
||||||
result = GetWXPeer()->OSXHandleKeyEvent(wxevent);
|
result = GetWXPeer()->OSXHandleKeyEvent(wxevent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...and wxEVT_CHAR.
|
// ...and wxEVT_CHAR.
|
||||||
result = result || DoHandleCharEvent(m_lastKeyDownEvent, text);
|
result = result || DoHandleCharEvent(GetLastNativeKeyDownEvent(), text);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2170,12 +2174,18 @@ void wxWidgetCocoaImpl::doCommandBySelector(void* sel, WXWidget slf, void* _cmd)
|
|||||||
wxLogTrace(TRACE_KEYS, "Selector %s for %s",
|
wxLogTrace(TRACE_KEYS, "Selector %s for %s",
|
||||||
wxDumpSelector((SEL)sel), wxDumpNSView(slf));
|
wxDumpSelector((SEL)sel), wxDumpNSView(slf));
|
||||||
|
|
||||||
if ( m_lastKeyDownEvent!=NULL )
|
// keystrokes can be translated on macos to selectors, see
|
||||||
|
// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/TextDefaultsBindings/TextDefaultsBindings.html
|
||||||
|
// it is also possible to map 1 keystroke to multiple commands, eg Ctrl-O on mac is translated to the bash-equivalent of
|
||||||
|
// execute and move back in history, since this results in two commands, Ctrl-O was sent twice as a wx key down event.
|
||||||
|
// we now track the sending of the events to avoid duplicates.
|
||||||
|
|
||||||
|
if ( IsInNativeKeyDown() && !WasKeyDownSent())
|
||||||
{
|
{
|
||||||
// If we have a corresponding key event, send wxEVT_KEY_DOWN now.
|
// If we have a corresponding key event, send wxEVT_KEY_DOWN now.
|
||||||
// (see also: wxWidgetCocoaImpl::DoHandleKeyEvent)
|
// (see also: wxWidgetCocoaImpl::DoHandleKeyEvent)
|
||||||
wxKeyEvent wxevent(wxEVT_KEY_DOWN);
|
wxKeyEvent wxevent(wxEVT_KEY_DOWN);
|
||||||
SetupKeyEvent( wxevent, m_lastKeyDownEvent );
|
SetupKeyEvent( wxevent, GetLastNativeKeyDownEvent() );
|
||||||
bool result = GetWXPeer()->OSXHandleKeyEvent(wxevent);
|
bool result = GetWXPeer()->OSXHandleKeyEvent(wxevent);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
@@ -2184,9 +2194,10 @@ void wxWidgetCocoaImpl::doCommandBySelector(void* sel, WXWidget slf, void* _cmd)
|
|||||||
|
|
||||||
wxKeyEvent wxevent2(wxevent) ;
|
wxKeyEvent wxevent2(wxevent) ;
|
||||||
wxevent2.SetEventType(wxEVT_CHAR);
|
wxevent2.SetEventType(wxEVT_CHAR);
|
||||||
SetupKeyEvent( wxevent2, m_lastKeyDownEvent );
|
SetupKeyEvent( wxevent2, GetLastNativeKeyDownEvent() );
|
||||||
GetWXPeer()->OSXHandleKeyEvent(wxevent2);
|
GetWXPeer()->OSXHandleKeyEvent(wxevent2);
|
||||||
}
|
}
|
||||||
|
SetKeyDownSent();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2544,7 +2555,8 @@ void wxWidgetCocoaImpl::Init()
|
|||||||
#if !wxOSX_USE_NATIVE_FLIPPED
|
#if !wxOSX_USE_NATIVE_FLIPPED
|
||||||
m_isFlipped = true;
|
m_isFlipped = true;
|
||||||
#endif
|
#endif
|
||||||
m_lastKeyDownEvent = NULL;
|
m_lastKeyDownEvent = nil;
|
||||||
|
m_lastKeyDownWXSent = false;
|
||||||
m_hasEditor = false;
|
m_hasEditor = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2568,6 +2580,54 @@ wxWidgetCocoaImpl::~wxWidgetCocoaImpl()
|
|||||||
wxCocoaGestures::EraseForObject(this);
|
wxCocoaGestures::EraseForObject(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxWidgetCocoaImpl::BeginNativeKeyDownEvent( NSEvent* event )
|
||||||
|
{
|
||||||
|
m_lastKeyDownEvent = event;
|
||||||
|
m_lastKeyDownWXSent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxWidgetCocoaImpl::EndNativeKeyDownEvent()
|
||||||
|
{
|
||||||
|
m_lastKeyDownEvent = nil;
|
||||||
|
m_lastKeyDownWXSent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxWidgetCocoaImpl::IsInNativeKeyDown() const
|
||||||
|
{
|
||||||
|
return m_lastKeyDownEvent != nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSEvent* wxWidgetCocoaImpl::GetLastNativeKeyDownEvent()
|
||||||
|
{
|
||||||
|
wxASSERT( m_lastKeyDownEvent != nil);
|
||||||
|
return m_lastKeyDownEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxWidgetCocoaImpl::SetKeyDownSent()
|
||||||
|
{
|
||||||
|
wxASSERT( !m_lastKeyDownWXSent );
|
||||||
|
m_lastKeyDownWXSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxWidgetCocoaImpl::WasKeyDownSent() const
|
||||||
|
{
|
||||||
|
return m_lastKeyDownWXSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxWidgetCocoaNativeKeyDownSuspender::wxWidgetCocoaNativeKeyDownSuspender( wxWidgetCocoaImpl *target ) : m_target(target)
|
||||||
|
{
|
||||||
|
m_nsevent = m_target->m_lastKeyDownEvent;
|
||||||
|
m_wxsent = m_target->m_lastKeyDownWXSent;
|
||||||
|
|
||||||
|
m_target->m_lastKeyDownEvent = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxWidgetCocoaNativeKeyDownSuspender::~wxWidgetCocoaNativeKeyDownSuspender()
|
||||||
|
{
|
||||||
|
m_target->m_lastKeyDownEvent = m_nsevent;
|
||||||
|
m_target->m_lastKeyDownWXSent = m_wxsent;
|
||||||
|
}
|
||||||
|
|
||||||
bool wxWidgetCocoaImpl::IsVisible() const
|
bool wxWidgetCocoaImpl::IsVisible() const
|
||||||
{
|
{
|
||||||
return [m_osxView isHiddenOrHasHiddenAncestor] == NO;
|
return [m_osxView isHiddenOrHasHiddenAncestor] == NO;
|
||||||
|
Reference in New Issue
Block a user