simplified and cleaned up wxGTK's focus handling

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@52473 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Václav Slavík
2008-03-14 01:10:43 +00:00
parent fe828a03f4
commit bd2e08d065
9 changed files with 226 additions and 325 deletions

View File

@@ -185,11 +185,13 @@ extern wxCursor g_globalCursor;
static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL;
static bool g_captureWindowHasMouse = false;
wxWindowGTK *g_focusWindow = (wxWindowGTK*) NULL;
// The window that currently has focus or is scheduled to get it in the next
// event loop iteration
static wxWindowGTK *gs_focusWindow = NULL;
// If a window get the focus set but has not been realized
// yet, defer setting the focus to idle time.
wxWindowGTK *g_delayedFocus = (wxWindowGTK*) NULL;
// the window that has deferred focus-out event pending, if any (see
// GTKAddDeferredFocusOut() for details)
static wxWindowGTK *gs_deferredFocusOut = NULL;
// global variables because GTK+ DnD want to have the
// mouse event that caused it
@@ -225,31 +227,6 @@ gdk_window_warp_pointer (GdkWindow *window,
}
}
//-----------------------------------------------------------------------------
// local code (see below)
//-----------------------------------------------------------------------------
// returns the child of win which currently has focus or NULL if not found
static wxWindow *wxFindFocusedChild(wxWindowGTK *win)
{
wxWindow *winFocus = wxWindowGTK::FindFocus();
if ( !winFocus )
return (wxWindow *)NULL;
if ( winFocus == win )
return (wxWindow *)win;
for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
node;
node = node->GetNext() )
{
wxWindow *child = wxFindFocusedChild(node->GetData());
if ( child )
return child;
}
return (wxWindow *)NULL;
}
//-----------------------------------------------------------------------------
// "size_request" of m_widget
@@ -1251,21 +1228,6 @@ wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing)
if ( rc != -1 ) \
return rc
// send the wxChildFocusEvent and wxFocusEvent, common code of
// gtk_window_focus_in_callback() and SetFocus()
static bool DoSendFocusEvents(wxWindow *win)
{
// Notify the parent keeping track of focus for the kbd navigation
// purposes that we got it.
wxChildFocusEvent eventChildFocus(win);
(void)win->HandleWindowEvent(eventChildFocus);
wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
eventFocus.SetEventObject(win);
return win->HandleWindowEvent(eventFocus);
}
// all event handlers must have C linkage as they're called from GTK+ C code
extern "C"
{
@@ -1410,7 +1372,7 @@ gtk_window_button_press_callback( GtkWidget *widget,
return TRUE;
if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
(g_focusWindow != win) /* && win->IsFocusable() */)
(gs_focusWindow != win) /* && win->IsFocusable() */)
{
win->SetFocus();
}
@@ -1610,44 +1572,9 @@ static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
static gboolean
gtk_window_focus_in_callback( GtkWidget * WXUNUSED(widget),
GdkEventFocus *WXUNUSED(event),
wxWindow *win )
wxWindowGTK *win )
{
if (win->m_imData)
gtk_im_context_focus_in(win->m_imData->context);
g_focusWindow = win;
wxLogTrace(TRACE_FOCUS,
_T("%s: focus in"), win->GetName().c_str());
#if wxUSE_CARET
// caret needs to be informed about focus change
wxCaret *caret = win->GetCaret();
if ( caret )
{
caret->OnSetFocus();
}
#endif // wxUSE_CARET
gboolean ret = FALSE;
// does the window itself think that it has the focus?
if ( !win->m_hasFocus )
{
// not yet, notify it
win->m_hasFocus = true;
(void)DoSendFocusEvents(win);
ret = TRUE;
}
// Disable default focus handling for custom windows
// since the default GTK+ handler issues a repaint
if (win->m_wxwindow)
return ret;
return FALSE;
return win->GTKHandleFocusIn();
}
//-----------------------------------------------------------------------------
@@ -1659,53 +1586,13 @@ gtk_window_focus_out_callback( GtkWidget * WXUNUSED(widget),
GdkEventFocus * WXUNUSED(gdk_event),
wxWindowGTK *win )
{
if (win->m_imData)
gtk_im_context_focus_out(win->m_imData->context);
wxLogTrace( TRACE_FOCUS,
_T("%s: focus out"), win->GetName().c_str() );
wxWindowGTK *winFocus = wxFindFocusedChild(win);
if ( winFocus )
win = winFocus;
g_focusWindow = (wxWindowGTK *)NULL;
#if wxUSE_CARET
// caret needs to be informed about focus change
wxCaret *caret = win->GetCaret();
if ( caret )
{
caret->OnKillFocus();
}
#endif // wxUSE_CARET
// don't send the window a kill focus event if it thinks that it doesn't
// have focus already
if ( win->m_hasFocus )
{
// the event handler might delete the window when it loses focus, so
// check whether this is a custom window before calling it
const bool has_wxwindow = win->m_wxwindow != NULL;
win->m_hasFocus = false;
wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
event.SetEventObject( win );
(void)win->GTKProcessEvent( event );
// Disable default focus handling for custom windows
// since the default GTK+ handler issues a repaint
if ( has_wxwindow )
return TRUE;
}
// continue with normal processing
return FALSE;
return win->GTKHandleFocusOut();
}
//-----------------------------------------------------------------------------
// "focus"
//-----------------------------------------------------------------------------
static gboolean
wx_window_focus_callback(GtkWidget *widget,
GtkDirectionType WXUNUSED(direction),
@@ -2005,7 +1892,7 @@ public:
wxWindow *wxWindowBase::DoFindFocus()
{
// the cast is necessary when we compile in wxUniversal mode
return (wxWindow *)g_focusWindow;
return wx_static_cast(wxWindow*, gs_focusWindow);
}
//-----------------------------------------------------------------------------
@@ -2117,8 +2004,6 @@ void wxWindowGTK::Init()
m_insertCallback = wxInsertChildInWindow;
m_hasFocus = false;
m_clipPaintRegion = false;
m_needsStyleChange = false;
@@ -2251,11 +2136,11 @@ wxWindowGTK::~wxWindowGTK()
{
SendDestroyEvent();
if (g_focusWindow == this)
g_focusWindow = NULL;
if (gs_focusWindow == this)
gs_focusWindow = NULL;
if ( g_delayedFocus == this )
g_delayedFocus = NULL;
if ( gs_deferredFocusOut == this )
gs_deferredFocusOut = NULL;
m_isBeingDeleted = true;
m_hasVMT = false;
@@ -2614,6 +2499,9 @@ bool wxWindowGTK::GtkShowFromOnIdle()
void wxWindowGTK::OnInternalIdle()
{
if ( gs_deferredFocusOut )
GTKHandleDeferredFocusOut();
// Check if we have to show window now
if (GtkShowFromOnIdle()) return;
@@ -2979,90 +2867,194 @@ void wxWindowGTK::GetTextExtent( const wxString& string,
g_object_unref (layout);
}
bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
{
if ( g_delayedFocus == this )
{
if ( GTK_WIDGET_REALIZED(m_widget) )
{
gtk_widget_grab_focus(m_widget);
g_delayedFocus = NULL;
return true;
bool wxWindowGTK::GTKHandleFocusIn()
{
// Disable default focus handling for custom windows since the default GTK+
// handler issues a repaint
const bool retval = m_wxwindow ? true : false;
// NB: if there's still unprocessed deferred focus-out event (see
// GTKHandleFocusOut() for explanation), we need to process it first so
// that the order of focus events -- focus-out first, then focus-in
// elsewhere -- is preserved
if ( gs_deferredFocusOut )
{
if ( GTKNeedsToFilterSameWindowFocus() &&
gs_deferredFocusOut == this )
{
// GTK+ focus changed from this wxWindow back to itself, so don't
// emit any events at all
wxLogTrace(TRACE_FOCUS,
"filtered out spurious focus change within %s(%p, %s)",
GetClassInfo()->GetClassName(), this, GetLabel());
gs_deferredFocusOut = NULL;
return retval;
}
// otherwise we need to send focus-out first
wxASSERT_MSG ( gs_deferredFocusOut != this,
"GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
GTKHandleDeferredFocusOut();
}
return false;
wxLogTrace(TRACE_FOCUS,
"handling focus_in event for %s(%p, %s)",
GetClassInfo()->GetClassName(), this, GetLabel());
if (m_imData)
gtk_im_context_focus_in(m_imData->context);
// NB: SetFocus() does this assignment too, but not all focus changes
// originate from SetFocus() call
gs_focusWindow = this;
#if wxUSE_CARET
// caret needs to be informed about focus change
wxCaret *caret = GetCaret();
if ( caret )
{
caret->OnSetFocus();
}
#endif // wxUSE_CARET
// Notify the parent keeping track of focus for the kbd navigation
// purposes that we got it.
wxChildFocusEvent eventChildFocus(this);
GTKProcessEvent(eventChildFocus);
wxFocusEvent eventFocus(wxEVT_SET_FOCUS, GetId());
eventFocus.SetEventObject(this);
GTKProcessEvent(eventFocus);
return retval;
}
bool wxWindowGTK::GTKHandleFocusOut()
{
// Disable default focus handling for custom windows since the default GTK+
// handler issues a repaint
const bool retval = m_wxwindow ? true : false;
// NB: If a control is composed of several GtkWidgets and when focus
// changes from one of them to another within the same wxWindow, we get
// a focus-out event followed by focus-in for another GtkWidget owned
// by the same wx control. We don't want to generate two spurious
// wxEVT_SET_FOCUS events in this case, so we defer sending wx events
// from GTKHandleFocusOut() until we know for sure it's not coming back
// (i.e. in GTKHandleFocusIn() or at idle time).
if ( GTKNeedsToFilterSameWindowFocus() )
{
wxASSERT_MSG( gs_deferredFocusOut == NULL,
"deferred focus out event already pending" );
wxLogTrace(TRACE_FOCUS,
"deferring focus_out event for %s(%p, %s)",
GetClassInfo()->GetClassName(), this, GetLabel());
gs_deferredFocusOut = this;
return retval;
}
GTKHandleFocusOutNoDeferring();
return retval;
}
void wxWindowGTK::GTKHandleFocusOutNoDeferring()
{
wxLogTrace(TRACE_FOCUS,
"handling focus_out event for %s(%p, %s)",
GetClassInfo()->GetClassName(), this, GetLabel());
if (m_imData)
gtk_im_context_focus_out(m_imData->context);
if ( gs_focusWindow != this )
{
// Something is terribly wrong, gs_focusWindow is out of sync with the
// real focus. We will reset it to NULL anyway, because after this
// focus-out event is handled, one of the following with happen:
//
// * either focus will go out of the app altogether, in which case
// gs_focusWindow _should_ be NULL
//
// * or it goes to another control, in which case focus-in event will
// follow immediately and it will set gs_focusWindow to the right
// value
wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
GetClassInfo()->GetClassName(), this, GetLabel());
}
gs_focusWindow = NULL;
#if wxUSE_CARET
// caret needs to be informed about focus change
wxCaret *caret = GetCaret();
if ( caret )
{
caret->OnKillFocus();
}
#endif // wxUSE_CARET
wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
event.SetEventObject( this );
GTKProcessEvent( event );
}
/*static*/
void wxWindowGTK::GTKHandleDeferredFocusOut()
{
// NB: See GTKHandleFocusOut() for explanation. This function is called
// from either GTKHandleFocusIn() or OnInternalIdle() to process
// deferred event.
if ( gs_deferredFocusOut )
{
wxWindowGTK *win = gs_deferredFocusOut;
gs_deferredFocusOut = NULL;
wxLogTrace(TRACE_FOCUS,
"processing deferred focus_out event for %s(%p, %s)",
win->GetClassInfo()->GetClassName(), win, win->GetLabel());
win->GTKHandleFocusOutNoDeferring();
}
}
void wxWindowGTK::SetFocus()
{
wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
if ( m_hasFocus )
{
// don't do anything if we already have focus
return;
}
if (m_wxwindow)
{
// wxWindow::SetFocus() should really set the focus to
// this control, whatever the flags are
if (!GTK_WIDGET_CAN_FOCUS(m_wxwindow))
GTK_WIDGET_SET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
// Setting "physical" focus is not immediate in GTK+ and while
// gtk_widget_is_focus ("determines if the widget is the focus widget
// within its toplevel", i.e. returns true for one widget per TLW, not
// globally) returns true immediately after grabbing focus,
// GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
// has focus at the moment) takes affect only after the window is shown
// (if it was hidden at the moment of the call) or at the next event loop
// iteration.
//
// Because we want to FindFocus() call immediately following
// foo->SetFocus() to return foo, we have to keep track of "pending" focus
// ourselves.
gs_focusWindow = this;
if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
{
gtk_widget_grab_focus (m_wxwindow);
}
GtkWidget *widget = m_wxwindow ? m_wxwindow : m_focusWidget;
if ( GTK_IS_CONTAINER(widget) &&
!GTK_WIDGET_CAN_FOCUS(widget) )
{
wxLogTrace(TRACE_FOCUS,
_T("Setting focus to a child of %s(%p, %s)"),
GetClassInfo()->GetClassName(), this, GetLabel().c_str());
gtk_widget_child_focus(widget, GTK_DIR_TAB_FORWARD);
}
else
{
// wxWindow::SetFocus() should really set the focus to
// this control, whatever the flags are
if (!GTK_WIDGET_CAN_FOCUS(m_widget))
GTK_WIDGET_SET_FLAGS(m_widget, GTK_CAN_FOCUS);
if (GTK_IS_CONTAINER(m_widget))
{
if (GTK_IS_RADIO_BUTTON(m_widget))
{
gtk_widget_grab_focus (m_widget);
return;
}
gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
}
else
if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
{
if (!GTK_WIDGET_REALIZED(m_widget))
{
// we can't set the focus to the widget now so we remember that
// it should be focused and will do it later, during the idle
// time, as soon as we can
wxLogTrace(TRACE_FOCUS,
_T("Delaying setting focus to %s(%s)"),
GetClassInfo()->GetClassName(), GetLabel().c_str());
g_delayedFocus = this;
}
else
{
wxLogTrace(TRACE_FOCUS,
_T("Setting focus to %s(%s)"),
GetClassInfo()->GetClassName(), GetLabel().c_str());
gtk_widget_grab_focus (m_widget);
}
}
else
{
wxLogTrace(TRACE_FOCUS,
_T("Can't set focus to %s(%s)"),
GetClassInfo()->GetClassName(), GetLabel().c_str());
}
wxLogTrace(TRACE_FOCUS,
_T("Setting focus to %s(%p, %s)"),
GetClassInfo()->GetClassName(), this, GetLabel().c_str());
gtk_widget_grab_focus(widget);
}
}