From: Vadim Zeitlin Date: Thu, 5 Apr 2007 22:29:14 +0000 (+0000) Subject: make wxControlContainer accept focus depending on whether it has any focusable childr... X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/80332672ab36153aa2424cff2a06b434287488c8 make wxControlContainer accept focus depending on whether it has any focusable children when using native TAB navigation too but also allow to manually override this automatic detection; added wxWindow::SetCanFocus() to notify GTK+ about changed focus state git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@45267 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/containr.h b/include/wx/containr.h index 551c7c63f1..e44b507a33 100644 --- a/include/wx/containr.h +++ b/include/wx/containr.h @@ -15,15 +15,126 @@ #include "wx/defs.h" +/* + Implementation note: wxControlContainer is not a real mix-in but rather + a class meant to be aggregated with (and not inherited from). Although + logically it should be a mix-in, doing it like this has no advantage from + the point of view of the existing code but does have some problems (we'd + need to play tricks with event handlers which may be difficult to do + safely). The price we pay for this simplicity is the ugly macros below. + */ + +// ---------------------------------------------------------------------------- +// wxControlContainerBase: common part used in both native and generic cases +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxControlContainerBase +{ +public: + // default ctor, SetContainerWindow() must be called later + wxControlContainerBase() + { + m_winParent = NULL; + + // do accept focus initially, we'll stop doing it if/when any children + // are added + m_acceptsFocus = true; + } + + void SetContainerWindow(wxWindow *winParent) + { + wxASSERT_MSG( !m_winParent, _T("shouldn't be called twice") ); + + m_winParent = winParent; + } + + // should be called when we decide that we should [stop] accepting focus + void SetCanFocus(bool acceptsFocus); + + // returns whether we should accept focus ourselves or not + bool AcceptsFocus() const { return m_acceptsFocus; } + + // call this when the number of children of the window changes + void UpdateCanFocus() { SetCanFocus(ShouldAcceptFocus()); } + +protected: + // return true if we should be focusable + bool ShouldAcceptFocus() const; + +private: + // the parent window we manage the children for + wxWindow *m_winParent; + + // value returned by AcceptsFocus(), should be changed using SetCanFocus() + // only + bool m_acceptsFocus; +}; + +// common part of WX_DECLARE_CONTROL_CONTAINER in the native and generic cases, +// it should be used in the wxWindow-derived class declaration +#define WX_DECLARE_CONTROL_CONTAINER_BASE() \ +public: \ + virtual bool AcceptsFocus() const; \ + virtual void AddChild(wxWindowBase *child); \ + virtual void RemoveChild(wxWindowBase *child); \ + void SetFocusIgnoringChildren(); \ + void AcceptFocus(bool acceptFocus) \ + { \ + m_container.SetCanFocus(acceptFocus); \ + } \ + \ +protected: \ + wxControlContainer m_container + +// this macro must be used in the derived class ctor +#define WX_INIT_CONTROL_CONTAINER() \ + m_container.SetContainerWindow(this) + +// common part of WX_DELEGATE_TO_CONTROL_CONTAINER in the native and generic +// cases, must be used in the wxWindow-derived class implementation +#define WX_DELEGATE_TO_CONTROL_CONTAINER_BASE(classname, basename) \ + void classname::AddChild(wxWindowBase *child) \ + { \ + basename::AddChild(child); \ + \ + m_container.UpdateCanFocus(); \ + } \ + \ + bool classname::AcceptsFocus() const \ + { \ + return m_container.AcceptsFocus(); \ + } + + #ifdef wxHAS_NATIVE_TAB_TRAVERSAL -#define WX_DECLARE_CONTROL_CONTAINER() \ - virtual bool AcceptsFocus() const { return false; } \ - void SetFocusIgnoringChildren() { SetFocus(); } +// ---------------------------------------------------------------------------- +// wxControlContainer for native TAB navigation +// ---------------------------------------------------------------------------- + +// this must be a real class as we forward-declare it elsewhere +class WXDLLEXPORT wxControlContainer : public wxControlContainerBase +{ +}; -#define WX_INIT_CONTROL_CONTAINER() #define WX_EVENT_TABLE_CONTROL_CONTAINER(classname) -#define WX_DELEGATE_TO_CONTROL_CONTAINER(classname, basename) + +#define WX_DECLARE_CONTROL_CONTAINER WX_DECLARE_CONTROL_CONTAINER_BASE + +#define WX_DELEGATE_TO_CONTROL_CONTAINER(classname, basename) \ + WX_DELEGATE_TO_CONTROL_CONTAINER_BASE(classname, basename) \ + \ + void classname::RemoveChild(wxWindowBase *child) \ + { \ + basename::RemoveChild(child); \ + \ + m_container.UpdateCanFocus(); \ + } \ + \ + void classname::SetFocusIgnoringChildren() \ + { \ + SetFocus(); \ + } #else // !wxHAS_NATIVE_TAB_TRAVERSAL @@ -32,25 +143,15 @@ class WXDLLEXPORT wxNavigationKeyEvent; class WXDLLEXPORT wxWindow; class WXDLLEXPORT wxWindowBase; -/* - Implementation note: wxControlContainer is not a real mix-in but rather - a class meant to be aggregated with (and not inherited from). Although - logically it should be a mix-in, doing it like this has no advantage from - the point of view of the existing code but does have some problems (we'd - need to play tricks with event handlers which may be difficult to do - safely). The price we pay for this simplicity is the ugly macros below. - */ - // ---------------------------------------------------------------------------- -// wxControlContainer +// wxControlContainer for TAB navigation implemented in wx itself // ---------------------------------------------------------------------------- -class WXDLLEXPORT wxControlContainer +class WXDLLEXPORT wxControlContainer : public wxControlContainerBase { public: - // ctors and such - wxControlContainer(wxWindow *winParent = NULL); - void SetContainerWindow(wxWindow *winParent) { m_winParent = winParent; } + // default ctor, SetContainerWindow() must be called later + wxControlContainer(); // the methods to be called from the window event handlers void HandleOnNavigationKey(wxNavigationKeyEvent& event); @@ -72,9 +173,6 @@ protected: // set the focus to the child which had it the last time bool SetFocusToChild(); - // the parent window we manage the children for - wxWindow *m_winParent; - // the child which had the focus last time this panel was activated wxWindow *m_winLastFocused; @@ -97,19 +195,15 @@ extern bool wxSetFocusToChild(wxWindow *win, wxWindow **child); public: \ void OnNavigationKey(wxNavigationKeyEvent& event); \ void OnFocus(wxFocusEvent& event); \ + void SetFocusIgnoringChildren(); \ virtual void OnChildFocus(wxChildFocusEvent& event); \ virtual void SetFocus(); \ - virtual void SetFocusIgnoringChildren(); \ virtual void RemoveChild(wxWindowBase *child); \ virtual bool AcceptsFocus() const; \ \ protected: \ wxControlContainer m_container -// this macro must be used in the derived class ctor -#define WX_INIT_CONTROL_CONTAINER() \ - m_container.SetContainerWindow(this) - // implement the event table entries for wxControlContainer #define WX_EVENT_TABLE_CONTROL_CONTAINER(classname) \ EVT_SET_FOCUS(classname::OnFocus) \ @@ -117,43 +211,43 @@ protected: \ EVT_NAVIGATION_KEY(classname::OnNavigationKey) // implement the methods forwarding to the wxControlContainer -#define WX_DELEGATE_TO_CONTROL_CONTAINER(classname, basename) \ -void classname::OnNavigationKey( wxNavigationKeyEvent& event ) \ -{ \ - m_container.HandleOnNavigationKey(event); \ -} \ - \ -void classname::RemoveChild(wxWindowBase *child) \ -{ \ - m_container.HandleOnWindowDestroy(child); \ - \ - basename::RemoveChild(child); \ -} \ - \ -void classname::SetFocus() \ -{ \ - if ( !m_container.DoSetFocus() ) \ - basename::SetFocus(); \ -} \ - \ -void classname::SetFocusIgnoringChildren() \ -{ \ - basename::SetFocus(); \ -} \ - \ -void classname::OnChildFocus(wxChildFocusEvent& event) \ -{ \ - m_container.SetLastFocus(event.GetWindow()); \ -} \ - \ -void classname::OnFocus(wxFocusEvent& event) \ -{ \ - m_container.HandleOnFocus(event); \ -} \ -bool classname::AcceptsFocus() const \ -{ \ - return m_container.AcceptsFocus(); \ -} +#define WX_DELEGATE_TO_CONTROL_CONTAINER(classname, basename) \ + WX_DELEGATE_TO_CONTROL_CONTAINER_BASE(classname, basename) \ + \ + void classname::RemoveChild(wxWindowBase *child) \ + { \ + m_container.HandleOnWindowDestroy(child); \ + \ + basename::RemoveChild(child); \ + \ + m_container.UpdateCanFocus(); \ + } \ + \ + void classname::OnNavigationKey( wxNavigationKeyEvent& event ) \ + { \ + m_container.HandleOnNavigationKey(event); \ + } \ + \ + void classname::SetFocus() \ + { \ + if ( !m_container.DoSetFocus() ) \ + basename::SetFocus(); \ + } \ + \ + void classname::SetFocusIgnoringChildren() \ + { \ + basename::SetFocus(); \ + } \ + \ + void classname::OnChildFocus(wxChildFocusEvent& event) \ + { \ + m_container.SetLastFocus(event.GetWindow()); \ + } \ + \ + void classname::OnFocus(wxFocusEvent& event) \ + { \ + m_container.HandleOnFocus(event); \ + } #endif // wxHAS_NATIVE_TAB_TRAVERSAL/!wxHAS_NATIVE_TAB_TRAVERSAL diff --git a/include/wx/gtk/window.h b/include/wx/gtk/window.h index 82b9f99d8c..2646c5c4bf 100644 --- a/include/wx/gtk/window.h +++ b/include/wx/gtk/window.h @@ -68,6 +68,7 @@ public: virtual bool IsRetained() const; virtual void SetFocus(); + virtual void SetCanFocus(bool canFocus); virtual bool Reparent( wxWindowBase *newParent ); diff --git a/include/wx/window.h b/include/wx/window.h index 2dde62f41f..4c62a7d035 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -592,6 +592,9 @@ public: bool CanAcceptFocusFromKeyboard() const { return AcceptsFocusFromKeyboard() && CanAcceptFocus(); } + // call this when the return value of AcceptsFocus() changes + virtual void SetCanFocus(bool WXUNUSED(canFocus)) { } + // navigates inside this window bool NavigateIn(int flags = wxNavigationKeyEvent::IsForward) { return DoNavigateIn(flags); } diff --git a/src/common/containr.cpp b/src/common/containr.cpp index 84be03d146..bbc5d48b61 100644 --- a/src/common/containr.cpp +++ b/src/common/containr.cpp @@ -28,8 +28,6 @@ #include "wx/containr.h" #endif -#ifndef wxHAS_NATIVE_TAB_TRAVERSAL - #ifndef WX_PRECOMP #include "wx/log.h" #include "wx/event.h" @@ -45,14 +43,25 @@ // implementation // ============================================================================ -wxControlContainer::wxControlContainer(wxWindow *winParent) +// ---------------------------------------------------------------------------- +// wxControlContainerBase +// ---------------------------------------------------------------------------- + +void wxControlContainerBase::SetCanFocus(bool acceptsFocus) { - m_winParent = winParent; - m_winLastFocused = NULL; - m_inSetFocus = false; + if ( acceptsFocus == m_acceptsFocus ) + return; + + m_acceptsFocus = acceptsFocus; + + m_winParent->SetCanFocus(m_acceptsFocus); } -bool wxControlContainer::AcceptsFocus() const +// if the window has a focusable child, it shouldn't be focusable itself (think +// of wxPanel used for grouping different controls) but if it doesn't have any +// (focusable) children, then it should be possible to give it focus (think of +// wxGrid or generic wxListCtrl) +bool wxControlContainerBase::ShouldAcceptFocus() const { // we can accept focus either if we have no children at all (in this case // we're probably not used as a container) or only when at least one child @@ -61,12 +70,6 @@ bool wxControlContainer::AcceptsFocus() const if ( !node ) return true; -#ifdef __WXMAC__ - // wxMac has eventually the two scrollbars as children, they don't count - // as real children in the algorithm mentioned above - bool hasRealChildren = false ; -#endif - while ( node ) { wxWindow *child = node->GetData(); @@ -75,20 +78,25 @@ bool wxControlContainer::AcceptsFocus() const #ifdef __WXMAC__ if ( m_winParent->MacIsWindowScrollbar( child ) ) continue; - hasRealChildren = true ; #endif + if ( child->CanAcceptFocus() ) - { - return true; - } + return false; } -#ifdef __WXMAC__ - if ( !hasRealChildren ) - return true ; -#endif + return true; +} - return false; +#ifndef wxHAS_NATIVE_TAB_TRAVERSAL + +// ---------------------------------------------------------------------------- +// generic wxControlContainer +// ---------------------------------------------------------------------------- + +wxControlContainer::wxControlContainer() +{ + m_winLastFocused = NULL; + m_inSetFocus = false; } void wxControlContainer::SetLastFocus(wxWindow *win) diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 83368e7c24..7311613839 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -2532,9 +2532,7 @@ void wxWindowGTK::PostCreation() if ( !AcceptsFocusFromKeyboard() ) { - GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS ); - if (m_wxwindow && (m_widget != m_wxwindow)) - GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS ); + SetCanFocus(false); g_signal_connect(m_widget, "focus", G_CALLBACK(wx_window_focus_callback), this); @@ -3242,6 +3240,22 @@ void wxWindowGTK::SetFocus() } } +void wxWindowGTK::SetCanFocus(bool canFocus) +{ + if ( canFocus ) + GTK_WIDGET_SET_FLAGS(m_widget, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS(m_widget, GTK_CAN_FOCUS); + + if ( m_wxwindow && (m_widget != m_wxwindow) ) + { + if ( canFocus ) + GTK_WIDGET_SET_FLAGS(m_wxwindow, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS(m_wxwindow, GTK_CAN_FOCUS); + } +} + bool wxWindowGTK::Reparent( wxWindowBase *newParentBase ) { wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );