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
This commit is contained in:
Vadim Zeitlin
2007-04-05 22:29:14 +00:00
parent 30d560f4cf
commit 80332672ab
5 changed files with 212 additions and 92 deletions

View File

@@ -15,23 +15,6 @@
#include "wx/defs.h" #include "wx/defs.h"
#ifdef wxHAS_NATIVE_TAB_TRAVERSAL
#define WX_DECLARE_CONTROL_CONTAINER() \
virtual bool AcceptsFocus() const { return false; } \
void SetFocusIgnoringChildren() { SetFocus(); }
#define WX_INIT_CONTROL_CONTAINER()
#define WX_EVENT_TABLE_CONTROL_CONTAINER(classname)
#define WX_DELEGATE_TO_CONTROL_CONTAINER(classname, basename)
#else // !wxHAS_NATIVE_TAB_TRAVERSAL
class WXDLLEXPORT wxFocusEvent;
class WXDLLEXPORT wxNavigationKeyEvent;
class WXDLLEXPORT wxWindow;
class WXDLLEXPORT wxWindowBase;
/* /*
Implementation note: wxControlContainer is not a real mix-in but rather Implementation note: wxControlContainer is not a real mix-in but rather
a class meant to be aggregated with (and not inherited from). Although a class meant to be aggregated with (and not inherited from). Although
@@ -42,15 +25,133 @@ class WXDLLEXPORT wxWindowBase;
*/ */
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxControlContainer // wxControlContainerBase: common part used in both native and generic cases
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
class WXDLLEXPORT wxControlContainer class WXDLLEXPORT wxControlContainerBase
{ {
public: public:
// ctors and such // default ctor, SetContainerWindow() must be called later
wxControlContainer(wxWindow *winParent = NULL); wxControlContainerBase()
void SetContainerWindow(wxWindow *winParent) { m_winParent = winParent; } {
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
// ----------------------------------------------------------------------------
// wxControlContainer for native TAB navigation
// ----------------------------------------------------------------------------
// this must be a real class as we forward-declare it elsewhere
class WXDLLEXPORT wxControlContainer : public wxControlContainerBase
{
};
#define WX_EVENT_TABLE_CONTROL_CONTAINER(classname)
#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
class WXDLLEXPORT wxFocusEvent;
class WXDLLEXPORT wxNavigationKeyEvent;
class WXDLLEXPORT wxWindow;
class WXDLLEXPORT wxWindowBase;
// ----------------------------------------------------------------------------
// wxControlContainer for TAB navigation implemented in wx itself
// ----------------------------------------------------------------------------
class WXDLLEXPORT wxControlContainer : public wxControlContainerBase
{
public:
// default ctor, SetContainerWindow() must be called later
wxControlContainer();
// the methods to be called from the window event handlers // the methods to be called from the window event handlers
void HandleOnNavigationKey(wxNavigationKeyEvent& event); void HandleOnNavigationKey(wxNavigationKeyEvent& event);
@@ -72,9 +173,6 @@ protected:
// set the focus to the child which had it the last time // set the focus to the child which had it the last time
bool SetFocusToChild(); 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 // the child which had the focus last time this panel was activated
wxWindow *m_winLastFocused; wxWindow *m_winLastFocused;
@@ -97,19 +195,15 @@ extern bool wxSetFocusToChild(wxWindow *win, wxWindow **child);
public: \ public: \
void OnNavigationKey(wxNavigationKeyEvent& event); \ void OnNavigationKey(wxNavigationKeyEvent& event); \
void OnFocus(wxFocusEvent& event); \ void OnFocus(wxFocusEvent& event); \
void SetFocusIgnoringChildren(); \
virtual void OnChildFocus(wxChildFocusEvent& event); \ virtual void OnChildFocus(wxChildFocusEvent& event); \
virtual void SetFocus(); \ virtual void SetFocus(); \
virtual void SetFocusIgnoringChildren(); \
virtual void RemoveChild(wxWindowBase *child); \ virtual void RemoveChild(wxWindowBase *child); \
virtual bool AcceptsFocus() const; \ virtual bool AcceptsFocus() const; \
\ \
protected: \ protected: \
wxControlContainer m_container 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 // implement the event table entries for wxControlContainer
#define WX_EVENT_TABLE_CONTROL_CONTAINER(classname) \ #define WX_EVENT_TABLE_CONTROL_CONTAINER(classname) \
EVT_SET_FOCUS(classname::OnFocus) \ EVT_SET_FOCUS(classname::OnFocus) \
@@ -118,16 +212,20 @@ protected: \
// implement the methods forwarding to the wxControlContainer // implement the methods forwarding to the wxControlContainer
#define WX_DELEGATE_TO_CONTROL_CONTAINER(classname, basename) \ #define WX_DELEGATE_TO_CONTROL_CONTAINER(classname, basename) \
void classname::OnNavigationKey( wxNavigationKeyEvent& event ) \ WX_DELEGATE_TO_CONTROL_CONTAINER_BASE(classname, basename) \
{ \
m_container.HandleOnNavigationKey(event); \
} \
\ \
void classname::RemoveChild(wxWindowBase *child) \ void classname::RemoveChild(wxWindowBase *child) \
{ \ { \
m_container.HandleOnWindowDestroy(child); \ m_container.HandleOnWindowDestroy(child); \
\ \
basename::RemoveChild(child); \ basename::RemoveChild(child); \
\
m_container.UpdateCanFocus(); \
} \
\
void classname::OnNavigationKey( wxNavigationKeyEvent& event ) \
{ \
m_container.HandleOnNavigationKey(event); \
} \ } \
\ \
void classname::SetFocus() \ void classname::SetFocus() \
@@ -149,10 +247,6 @@ void classname::OnChildFocus(wxChildFocusEvent& event) \
void classname::OnFocus(wxFocusEvent& event) \ void classname::OnFocus(wxFocusEvent& event) \
{ \ { \
m_container.HandleOnFocus(event); \ m_container.HandleOnFocus(event); \
} \
bool classname::AcceptsFocus() const \
{ \
return m_container.AcceptsFocus(); \
} }
#endif // wxHAS_NATIVE_TAB_TRAVERSAL/!wxHAS_NATIVE_TAB_TRAVERSAL #endif // wxHAS_NATIVE_TAB_TRAVERSAL/!wxHAS_NATIVE_TAB_TRAVERSAL

View File

@@ -68,6 +68,7 @@ public:
virtual bool IsRetained() const; virtual bool IsRetained() const;
virtual void SetFocus(); virtual void SetFocus();
virtual void SetCanFocus(bool canFocus);
virtual bool Reparent( wxWindowBase *newParent ); virtual bool Reparent( wxWindowBase *newParent );

View File

@@ -592,6 +592,9 @@ public:
bool CanAcceptFocusFromKeyboard() const bool CanAcceptFocusFromKeyboard() const
{ return AcceptsFocusFromKeyboard() && CanAcceptFocus(); } { return AcceptsFocusFromKeyboard() && CanAcceptFocus(); }
// call this when the return value of AcceptsFocus() changes
virtual void SetCanFocus(bool WXUNUSED(canFocus)) { }
// navigates inside this window // navigates inside this window
bool NavigateIn(int flags = wxNavigationKeyEvent::IsForward) bool NavigateIn(int flags = wxNavigationKeyEvent::IsForward)
{ return DoNavigateIn(flags); } { return DoNavigateIn(flags); }

View File

@@ -28,8 +28,6 @@
#include "wx/containr.h" #include "wx/containr.h"
#endif #endif
#ifndef wxHAS_NATIVE_TAB_TRAVERSAL
#ifndef WX_PRECOMP #ifndef WX_PRECOMP
#include "wx/log.h" #include "wx/log.h"
#include "wx/event.h" #include "wx/event.h"
@@ -45,14 +43,25 @@
// implementation // implementation
// ============================================================================ // ============================================================================
wxControlContainer::wxControlContainer(wxWindow *winParent) // ----------------------------------------------------------------------------
// wxControlContainerBase
// ----------------------------------------------------------------------------
void wxControlContainerBase::SetCanFocus(bool acceptsFocus)
{ {
m_winParent = winParent; if ( acceptsFocus == m_acceptsFocus )
m_winLastFocused = NULL; return;
m_inSetFocus = false;
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 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 // 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 ) if ( !node )
return true; 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 ) while ( node )
{ {
wxWindow *child = node->GetData(); wxWindow *child = node->GetData();
@@ -75,22 +78,27 @@ bool wxControlContainer::AcceptsFocus() const
#ifdef __WXMAC__ #ifdef __WXMAC__
if ( m_winParent->MacIsWindowScrollbar( child ) ) if ( m_winParent->MacIsWindowScrollbar( child ) )
continue; continue;
hasRealChildren = true ;
#endif #endif
if ( child->CanAcceptFocus() ) if ( child->CanAcceptFocus() )
{
return true;
}
}
#ifdef __WXMAC__
if ( !hasRealChildren )
return true ;
#endif
return false; return false;
} }
return true;
}
#ifndef wxHAS_NATIVE_TAB_TRAVERSAL
// ----------------------------------------------------------------------------
// generic wxControlContainer
// ----------------------------------------------------------------------------
wxControlContainer::wxControlContainer()
{
m_winLastFocused = NULL;
m_inSetFocus = false;
}
void wxControlContainer::SetLastFocus(wxWindow *win) void wxControlContainer::SetLastFocus(wxWindow *win)
{ {
// the panel itself should never get the focus at all but if it does happen // the panel itself should never get the focus at all but if it does happen

View File

@@ -2532,9 +2532,7 @@ void wxWindowGTK::PostCreation()
if ( !AcceptsFocusFromKeyboard() ) if ( !AcceptsFocusFromKeyboard() )
{ {
GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS ); SetCanFocus(false);
if (m_wxwindow && (m_widget != m_wxwindow))
GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
g_signal_connect(m_widget, "focus", g_signal_connect(m_widget, "focus",
G_CALLBACK(wx_window_focus_callback), this); 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 ) bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
{ {
wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") ); wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );