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"
#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
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:
// ctors and such
wxControlContainer(wxWindow *winParent = NULL);
void SetContainerWindow(wxWindow *winParent) { m_winParent = winParent; }
// 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
// ----------------------------------------------------------------------------
// 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
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) \
@@ -118,42 +212,42 @@ protected: \
// implement the methods forwarding to the wxControlContainer
#define WX_DELEGATE_TO_CONTROL_CONTAINER(classname, basename) \
void classname::OnNavigationKey( wxNavigationKeyEvent& event ) \
{ \
m_container.HandleOnNavigationKey(event); \
} \
WX_DELEGATE_TO_CONTROL_CONTAINER_BASE(classname, basename) \
\
void classname::RemoveChild(wxWindowBase *child) \
{ \
void classname::RemoveChild(wxWindowBase *child) \
{ \
m_container.HandleOnWindowDestroy(child); \
\
basename::RemoveChild(child); \
} \
\
void classname::SetFocus() \
{ \
m_container.UpdateCanFocus(); \
} \
\
void classname::OnNavigationKey( wxNavigationKeyEvent& event ) \
{ \
m_container.HandleOnNavigationKey(event); \
} \
\
void classname::SetFocus() \
{ \
if ( !m_container.DoSetFocus() ) \
basename::SetFocus(); \
} \
} \
\
void classname::SetFocusIgnoringChildren() \
{ \
void classname::SetFocusIgnoringChildren() \
{ \
basename::SetFocus(); \
} \
} \
\
void classname::OnChildFocus(wxChildFocusEvent& event) \
{ \
void classname::OnChildFocus(wxChildFocusEvent& event) \
{ \
m_container.SetLastFocus(event.GetWindow()); \
} \
} \
\
void classname::OnFocus(wxFocusEvent& event) \
{ \
void classname::OnFocus(wxFocusEvent& event) \
{ \
m_container.HandleOnFocus(event); \
} \
bool classname::AcceptsFocus() const \
{ \
return m_container.AcceptsFocus(); \
}
}
#endif // wxHAS_NATIVE_TAB_TRAVERSAL/!wxHAS_NATIVE_TAB_TRAVERSAL

View File

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

View File

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

View File

@@ -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;
}
}
#ifdef __WXMAC__
if ( !hasRealChildren )
return true ;
#endif
return false;
}
return true;
}
#ifndef wxHAS_NATIVE_TAB_TRAVERSAL
// ----------------------------------------------------------------------------
// generic wxControlContainer
// ----------------------------------------------------------------------------
wxControlContainer::wxControlContainer()
{
m_winLastFocused = NULL;
m_inSetFocus = false;
}
void wxControlContainer::SetLastFocus(wxWindow *win)

View File

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