Tab navigation improvements for wxOSX
Closes https://github.com/wxWidgets/wxWidgets/pull/493
This commit is contained in:
commit
de3087c0de
@ -129,6 +129,8 @@ public :
|
|||||||
|
|
||||||
void InstallEventHandler( WXWidget control = NULL );
|
void InstallEventHandler( WXWidget control = NULL );
|
||||||
|
|
||||||
|
virtual bool ShouldHandleKeyNavigation(const wxKeyEvent &event) const;
|
||||||
|
bool DoHandleKeyNavigation(const wxKeyEvent &event);
|
||||||
virtual bool DoHandleMouseEvent(NSEvent *event);
|
virtual bool DoHandleMouseEvent(NSEvent *event);
|
||||||
virtual bool DoHandleKeyEvent(NSEvent *event);
|
virtual bool DoHandleKeyEvent(NSEvent *event);
|
||||||
virtual bool DoHandleCharEvent(NSEvent *event, NSString *text);
|
virtual bool DoHandleCharEvent(NSEvent *event, NSString *text);
|
||||||
|
@ -16,9 +16,27 @@
|
|||||||
|
|
||||||
@class wxTextEntryFormatter;
|
@class wxTextEntryFormatter;
|
||||||
|
|
||||||
|
class wxNSTextBase : public wxWidgetCocoaImpl, public wxTextWidgetImpl
|
||||||
|
{
|
||||||
|
public :
|
||||||
|
wxNSTextBase( wxTextCtrl *text, WXWidget w )
|
||||||
|
: wxWidgetCocoaImpl(text, w),
|
||||||
|
wxTextWidgetImpl(text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
wxNSTextBase( wxWindow *wxPeer, wxTextEntry *entry, WXWidget w )
|
||||||
|
: wxWidgetCocoaImpl(wxPeer, w),
|
||||||
|
wxTextWidgetImpl(entry)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~wxNSTextBase() { }
|
||||||
|
|
||||||
|
virtual bool ShouldHandleKeyNavigation(const wxKeyEvent &event) const wxOVERRIDE;
|
||||||
|
};
|
||||||
|
|
||||||
// implementation exposed, so that search control can pull it
|
// implementation exposed, so that search control can pull it
|
||||||
|
|
||||||
class wxNSTextFieldControl : public wxWidgetCocoaImpl, public wxTextWidgetImpl
|
class wxNSTextFieldControl : public wxNSTextBase
|
||||||
{
|
{
|
||||||
public :
|
public :
|
||||||
// wxNSTextFieldControl must always be associated with a wxTextEntry. If
|
// wxNSTextFieldControl must always be associated with a wxTextEntry. If
|
||||||
@ -67,7 +85,7 @@ private:
|
|||||||
wxTextEntryFormatter* GetFormatter();
|
wxTextEntryFormatter* GetFormatter();
|
||||||
};
|
};
|
||||||
|
|
||||||
class wxNSTextViewControl : public wxWidgetCocoaImpl, public wxTextWidgetImpl
|
class wxNSTextViewControl : public wxNSTextBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
wxNSTextViewControl( wxTextCtrl *wxPeer, WXWidget w, long style );
|
wxNSTextViewControl( wxTextCtrl *wxPeer, WXWidget w, long style );
|
||||||
|
@ -16,7 +16,7 @@ class WXDLLIMPEXP_FWD_CORE wxBitmap ;
|
|||||||
|
|
||||||
class WXDLLIMPEXP_FWD_CORE wxRadioButton ;
|
class WXDLLIMPEXP_FWD_CORE wxRadioButton ;
|
||||||
|
|
||||||
class WXDLLIMPEXP_CORE wxRadioBox: public wxControl, public wxRadioBoxBase
|
class WXDLLIMPEXP_CORE wxRadioBox: public wxNavigationEnabled<wxControl>, public wxRadioBoxBase
|
||||||
{
|
{
|
||||||
wxDECLARE_DYNAMIC_CLASS(wxRadioBox);
|
wxDECLARE_DYNAMIC_CLASS(wxRadioBox);
|
||||||
public:
|
public:
|
||||||
|
@ -38,6 +38,10 @@
|
|||||||
// trace mask for focus messages
|
// trace mask for focus messages
|
||||||
#define TRACE_FOCUS wxT("focus")
|
#define TRACE_FOCUS wxT("focus")
|
||||||
|
|
||||||
|
#if (defined(__WXMSW__) || defined(__WXMAC__)) && wxUSE_RADIOBTN
|
||||||
|
#define USE_RADIOBTN_NAV
|
||||||
|
#endif
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// implementation
|
// implementation
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -235,7 +239,7 @@ void wxControlContainer::SetLastFocus(wxWindow *win)
|
|||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
// The following four functions are used to find other radio buttons
|
// The following four functions are used to find other radio buttons
|
||||||
// within the same group. Used by wxSetFocusToChild on wxMSW
|
// within the same group. Used by wxSetFocusToChild
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
#if wxUSE_RADIOBTN
|
#if wxUSE_RADIOBTN
|
||||||
@ -348,7 +352,7 @@ wxRadioButton* wxGetSelectedButtonInGroup(wxRadioButton *btn)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __WXMSW__
|
#endif // wxUSE_RADIOBTN
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Keyboard handling - this is the place where the TAB traversal logic is
|
// Keyboard handling - this is the place where the TAB traversal logic is
|
||||||
@ -464,12 +468,12 @@ void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent& event )
|
|||||||
|
|
||||||
if ( winFocus )
|
if ( winFocus )
|
||||||
{
|
{
|
||||||
#if defined(__WXMSW__) && wxUSE_RADIOBTN
|
#if defined(USE_RADIOBTN_NAV)
|
||||||
// If we are in a radio button group, start from the first item in the
|
// If we are in a radio button group, start from the first item in the
|
||||||
// group
|
// group
|
||||||
if ( event.IsFromTab() && wxIsKindOf(winFocus, wxRadioButton ) )
|
if ( event.IsFromTab() && wxIsKindOf(winFocus, wxRadioButton ) )
|
||||||
winFocus = wxGetFirstButtonInGroup((wxRadioButton*)winFocus);
|
winFocus = wxGetFirstButtonInGroup((wxRadioButton*)winFocus);
|
||||||
#endif // __WXMSW__
|
#endif // USE_RADIOBTN_NAV
|
||||||
// ok, we found the focus - now is it our child?
|
// ok, we found the focus - now is it our child?
|
||||||
start_node = children.Find( winFocus );
|
start_node = children.Find( winFocus );
|
||||||
}
|
}
|
||||||
@ -554,7 +558,7 @@ void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent& event )
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__WXMSW__) && wxUSE_RADIOBTN
|
#if defined(USE_RADIOBTN_NAV)
|
||||||
if ( event.IsFromTab() )
|
if ( event.IsFromTab() )
|
||||||
{
|
{
|
||||||
if ( wxIsKindOf(child, wxRadioButton) )
|
if ( wxIsKindOf(child, wxRadioButton) )
|
||||||
@ -611,7 +615,7 @@ void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent& event )
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // __WXMSW__
|
#endif // USE_RADIOBTN_NAV
|
||||||
|
|
||||||
if ( child->CanAcceptFocusFromKeyboard() )
|
if ( child->CanAcceptFocusFromKeyboard() )
|
||||||
{
|
{
|
||||||
@ -748,7 +752,7 @@ bool wxSetFocusToChild(wxWindow *win, wxWindow **childLastFocused)
|
|||||||
|
|
||||||
if ( child->CanAcceptFocusFromKeyboard() && !child->IsTopLevel() )
|
if ( child->CanAcceptFocusFromKeyboard() && !child->IsTopLevel() )
|
||||||
{
|
{
|
||||||
#if defined(__WXMSW__) && wxUSE_RADIOBTN
|
#if defined(USE_RADIOBTN_NAV)
|
||||||
// If a radiobutton is the first focusable child, search for the
|
// If a radiobutton is the first focusable child, search for the
|
||||||
// selected radiobutton in the same group
|
// selected radiobutton in the same group
|
||||||
wxRadioButton* btn = wxDynamicCast(child, wxRadioButton);
|
wxRadioButton* btn = wxDynamicCast(child, wxRadioButton);
|
||||||
@ -758,7 +762,7 @@ bool wxSetFocusToChild(wxWindow *win, wxWindow **childLastFocused)
|
|||||||
if (selected)
|
if (selected)
|
||||||
child = selected;
|
child = selected;
|
||||||
}
|
}
|
||||||
#endif // __WXMSW__
|
#endif // USE_RADIOBTN_NAV
|
||||||
|
|
||||||
wxLogTrace(TRACE_FOCUS,
|
wxLogTrace(TRACE_FOCUS,
|
||||||
wxT("SetFocusToChild() => first child (0x%p)."),
|
wxT("SetFocusToChild() => first child (0x%p)."),
|
||||||
|
@ -279,6 +279,45 @@ NSView* wxMacEditHelper::ms_viewCurrentlyEdited = nil;
|
|||||||
handled = YES;
|
handled = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (wxpeer->GetWindowStyle() & wxTE_PASSWORD)
|
||||||
|
{
|
||||||
|
// Make TAB and ESCAPE work on password fields. The general fix done in wxWidgetCocoaImpl::DoHandleKeyEvent()
|
||||||
|
// does not help for password fields because wxWidgetCocoaImpl::keyEvent() is not executed
|
||||||
|
// when TAB is pressed, only when it is released.
|
||||||
|
wxKeyEvent wxevent(wxEVT_KEY_DOWN);
|
||||||
|
if (commandSelector == @selector(insertTab:))
|
||||||
|
{
|
||||||
|
wxevent.m_keyCode = WXK_TAB;
|
||||||
|
}
|
||||||
|
else if (commandSelector == @selector(insertBacktab:))
|
||||||
|
{
|
||||||
|
wxevent.m_keyCode = WXK_TAB;
|
||||||
|
wxevent.SetShiftDown(true);
|
||||||
|
}
|
||||||
|
else if (commandSelector == @selector(selectNextKeyView:))
|
||||||
|
{
|
||||||
|
wxevent.m_keyCode = WXK_TAB;
|
||||||
|
wxevent.SetRawControlDown(true);
|
||||||
|
}
|
||||||
|
else if (commandSelector == @selector(selectPreviousKeyView:))
|
||||||
|
{
|
||||||
|
wxevent.m_keyCode = WXK_TAB;
|
||||||
|
wxevent.SetShiftDown(true);
|
||||||
|
wxevent.SetRawControlDown(true);
|
||||||
|
}
|
||||||
|
else if (commandSelector == @selector(cancelOperation:))
|
||||||
|
{
|
||||||
|
wxevent.m_keyCode = WXK_ESCAPE;
|
||||||
|
}
|
||||||
|
if (wxevent.GetKeyCode() != WXK_NONE)
|
||||||
|
{
|
||||||
|
wxKeyEvent eventHook(wxEVT_CHAR_HOOK, wxevent);
|
||||||
|
if (wxpeer->OSXHandleKeyEvent(eventHook) && !eventHook.IsNextEventAllowed())
|
||||||
|
handled = YES;
|
||||||
|
else if (impl->DoHandleKeyNavigation(wxevent))
|
||||||
|
handled = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,6 +688,15 @@ NSView* wxMacEditHelper::ms_viewCurrentlyEdited = nil;
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
bool wxNSTextBase::ShouldHandleKeyNavigation(const wxKeyEvent &event) const
|
||||||
|
{
|
||||||
|
// Text controls must be allowed to handle the key even if wxWANTS_CHARS is not set, provided wxTE_PROCESS_TAB
|
||||||
|
// is set. To make Shift+TAB work with text controls we must process it here regardless of wxTE_PROCESS_TAB.
|
||||||
|
// For Ctrl(+Shift)+TAB to work as navigation key consistently in all types of text fields we must process it here as well.
|
||||||
|
return (!m_wxPeer->HasFlag(wxTE_PROCESS_TAB) || event.HasAnyModifiers());
|
||||||
|
}
|
||||||
|
|
||||||
// wxNSTextViewControl
|
// wxNSTextViewControl
|
||||||
|
|
||||||
// Official Apple docs suggest to use FLT_MAX when embedding an NSTextView
|
// Official Apple docs suggest to use FLT_MAX when embedding an NSTextView
|
||||||
@ -659,8 +707,7 @@ NSView* wxMacEditHelper::ms_viewCurrentlyEdited = nil;
|
|||||||
#define MAX_WIDTH 1000000
|
#define MAX_WIDTH 1000000
|
||||||
|
|
||||||
wxNSTextViewControl::wxNSTextViewControl( wxTextCtrl *wxPeer, WXWidget w, long style )
|
wxNSTextViewControl::wxNSTextViewControl( wxTextCtrl *wxPeer, WXWidget w, long style )
|
||||||
: wxWidgetCocoaImpl(wxPeer, w),
|
: wxNSTextBase(wxPeer, w)
|
||||||
wxTextWidgetImpl(wxPeer)
|
|
||||||
{
|
{
|
||||||
wxNSTextScrollView* sv = (wxNSTextScrollView*) w;
|
wxNSTextScrollView* sv = (wxNSTextScrollView*) w;
|
||||||
m_scrollView = sv;
|
m_scrollView = sv;
|
||||||
@ -966,8 +1013,7 @@ wxSize wxNSTextViewControl::GetBestSize() const
|
|||||||
// wxNSTextFieldControl
|
// wxNSTextFieldControl
|
||||||
|
|
||||||
wxNSTextFieldControl::wxNSTextFieldControl( wxTextCtrl *text, WXWidget w )
|
wxNSTextFieldControl::wxNSTextFieldControl( wxTextCtrl *text, WXWidget w )
|
||||||
: wxWidgetCocoaImpl(text, w),
|
: wxNSTextBase(text, w)
|
||||||
wxTextWidgetImpl(text)
|
|
||||||
{
|
{
|
||||||
Init(w);
|
Init(w);
|
||||||
}
|
}
|
||||||
@ -975,8 +1021,7 @@ wxNSTextFieldControl::wxNSTextFieldControl( wxTextCtrl *text, WXWidget w )
|
|||||||
wxNSTextFieldControl::wxNSTextFieldControl(wxWindow *wxPeer,
|
wxNSTextFieldControl::wxNSTextFieldControl(wxWindow *wxPeer,
|
||||||
wxTextEntry *entry,
|
wxTextEntry *entry,
|
||||||
WXWidget w)
|
WXWidget w)
|
||||||
: wxWidgetCocoaImpl(wxPeer, w),
|
: wxNSTextBase(wxPeer, entry, w)
|
||||||
wxTextWidgetImpl(entry)
|
|
||||||
{
|
{
|
||||||
Init(w);
|
Init(w);
|
||||||
}
|
}
|
||||||
|
@ -2833,6 +2833,41 @@ bool wxWidgetCocoaImpl::DoHandleCharEvent(NSEvent *event, NSString *text)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wxWidgetCocoaImpl::ShouldHandleKeyNavigation(const wxKeyEvent &WXUNUSED(event)) const
|
||||||
|
{
|
||||||
|
// Only controls that intercept tabs for different behavior should return false (ie wxTE_PROCESS_TAB)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxWidgetCocoaImpl::DoHandleKeyNavigation(const wxKeyEvent &event)
|
||||||
|
{
|
||||||
|
bool handled = false;
|
||||||
|
wxWindow *focus = GetWXPeer();
|
||||||
|
if (focus && event.GetKeyCode() == WXK_TAB)
|
||||||
|
{
|
||||||
|
if (ShouldHandleKeyNavigation(event))
|
||||||
|
{
|
||||||
|
wxWindow* iter = focus->GetParent() ;
|
||||||
|
while (iter && !handled)
|
||||||
|
{
|
||||||
|
if (iter->HasFlag(wxTAB_TRAVERSAL))
|
||||||
|
{
|
||||||
|
wxNavigationKeyEvent new_event;
|
||||||
|
new_event.SetEventObject( focus );
|
||||||
|
new_event.SetDirection( !event.ShiftDown() );
|
||||||
|
/* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
|
||||||
|
new_event.SetWindowChange( event.ControlDown() );
|
||||||
|
new_event.SetCurrentFocus( focus );
|
||||||
|
handled = iter->HandleWindowEvent( new_event ) && !new_event.GetSkipped();
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = iter->GetParent() ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)
|
bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)
|
||||||
{
|
{
|
||||||
wxKeyEvent wxevent(wxEVT_KEY_DOWN);
|
wxKeyEvent wxevent(wxEVT_KEY_DOWN);
|
||||||
@ -2847,6 +2882,9 @@ bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)
|
|||||||
if ( GetWXPeer()->OSXHandleKeyEvent(eventHook)
|
if ( GetWXPeer()->OSXHandleKeyEvent(eventHook)
|
||||||
&& !eventHook.IsNextEventAllowed() )
|
&& !eventHook.IsNextEventAllowed() )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (DoHandleKeyNavigation(wxevent))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( IsUserPane() && [event type] == NSKeyDown)
|
if ( IsUserPane() && [event type] == NSKeyDown)
|
||||||
|
Loading…
Reference in New Issue
Block a user