Preserve focus when window is minimized and restored in wxMSW.

Add specific code to save and restore the focus when the window is minimized
and restored in wxMSW as the existing code in WM_ACTIVATE handler wasn't
enough because this event was generated too late when minimizing the window
(when it was already minimized and so the focus had been already lost) and too
early when restoring it (so the window was still minimized and restoring focus
failed).

This is still not perfect as we do in our code something Windows would be
expected to do automatically but for whatever reason, it doesn't do it for
wxWidgets programs, and this manual workaround at least prevents the annoying
total focus loss.

Closes #1599.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@70513 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2012-02-05 14:18:28 +00:00
parent 7e08367534
commit 0c6dff0370
2 changed files with 88 additions and 40 deletions

View File

@@ -181,7 +181,16 @@ protected:
bool m_fsIsMaximized; bool m_fsIsMaximized;
bool m_fsIsShowing; bool m_fsIsShowing;
// the last focused child: we restore focus to it on activation // Save the current focus to m_winLastFocused if we're not iconized (the
// focus is always NULL when we're iconized).
void DoSaveLastFocus();
// Restore focus to m_winLastFocused if possible and needed.
void DoRestoreLastFocus();
// The last focused child: we remember it when we're deactivated and
// restore focus to it when we're activated (this is done here) or restored
// from iconic state (done by wxFrame).
wxWindow *m_winLastFocused; wxWindow *m_winLastFocused;
#if defined(__SMARTPHONE__) && defined(__WXWINCE__) #if defined(__SMARTPHONE__) && defined(__WXWINCE__)

View File

@@ -361,13 +361,6 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX
#endif // __SMARTPHONE__ || __POCKETPC__ #endif // __SMARTPHONE__ || __POCKETPC__
case WM_SYSCOMMAND: case WM_SYSCOMMAND:
// Keep the #ifdef block inside the case to fix a potential MSVC
// warning regarding switch statement containing no case or
// default labels (or a default only).
#ifndef __WXUNIVERSAL__
// We may need to generate events for the items added to the system
// menu if it had been created (and presumably modified).
if ( m_menuSystem )
{ {
// From MSDN: // From MSDN:
// //
@@ -378,15 +371,48 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX
// using the bitwise AND operator. // using the bitwise AND operator.
unsigned id = wParam & 0xfff0; unsigned id = wParam & 0xfff0;
// SC_SIZE is the first of the system-defined commands and we // Preserve the focus when minimizing/restoring the window: we
// leave those to DefWindowProc(). // need to do it manually as DefWindowProc() doesn't appear to
if ( id < SC_SIZE ) // do this for us for some reason (perhaps because we don't use
// WM_NEXTDLGCTL for setting focus?). Moreover, our code in
// OnActivate() doesn't work in this case as we receive the
// deactivation event too late when the window is being
// minimized and the focus is already NULL by then. Similarly,
// we receive the activation event too early and restoring
// focus in it fails because the window is still minimized. So
// we need to do it here.
if ( id == SC_MINIMIZE )
{
// For minimization, it's simple enough: just save the
// focus as usual. The important thing is that we're not
// minimized yet, so this works correctly.
DoSaveLastFocus();
}
else if ( id == SC_RESTORE )
{
// For restoring, it's trickier as DefWindowProc() sets
// focus to the window itself. So run it first and restore
// our saved focus only afterwards.
processed = true;
rc = wxTopLevelWindowBase::MSWWindowProc(message,
wParam, lParam);
DoRestoreLastFocus();
}
#ifndef __WXUNIVERSAL__
// We need to generate events for the custom items added to the
// system menu if it had been created (and presumably modified).
// As SC_SIZE is the first of the system-defined commands, we
// only do this for the custom commands before it and leave
// SC_SIZE and everything after it to DefWindowProc().
if ( m_menuSystem && id < SC_SIZE )
{ {
if ( m_menuSystem->MSWCommand(0 /* unused anyhow */, id) ) if ( m_menuSystem->MSWCommand(0 /* unused anyhow */, id) )
processed = true; processed = true;
} }
}
#endif // #ifndef __WXUNIVERSAL__ #endif // #ifndef __WXUNIVERSAL__
}
break; break;
} }
@@ -1338,47 +1364,60 @@ void wxTopLevelWindowMSW::DoThaw()
// wxTopLevelWindow event handling // wxTopLevelWindow event handling
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Default activation behaviour - set the focus for the first child void wxTopLevelWindowMSW::DoSaveLastFocus()
// subwindow found. {
if ( m_iconized )
return;
// remember the last focused child if it is our child
m_winLastFocused = FindFocus();
if ( m_winLastFocused )
{
// and don't remember it if it's a child from some other frame
if ( wxGetTopLevelParent(m_winLastFocused) != this )
{
m_winLastFocused = NULL;
}
}
}
void wxTopLevelWindowMSW::DoRestoreLastFocus()
{
wxWindow *parent = m_winLastFocused ? m_winLastFocused->GetParent()
: NULL;
if ( !parent )
{
parent = this;
}
wxSetFocusToChild(parent, &m_winLastFocused);
}
void wxTopLevelWindowMSW::OnActivate(wxActivateEvent& event) void wxTopLevelWindowMSW::OnActivate(wxActivateEvent& event)
{ {
if ( event.GetActive() ) if ( event.GetActive() )
{ {
// We get WM_ACTIVATE before being restored from iconized state, so we
// can be still iconized here. In this case, avoid restoring the focus
// as it doesn't work anyhow and we will do when we're really restored.
if ( m_iconized )
{
event.Skip();
return;
}
// restore focus to the child which was last focused unless we already // restore focus to the child which was last focused unless we already
// have it // have it
wxLogTrace(wxT("focus"), wxT("wxTLW %p activated."), m_hWnd); wxLogTrace(wxT("focus"), wxT("wxTLW %p activated."), m_hWnd);
wxWindow *winFocus = FindFocus(); wxWindow *winFocus = FindFocus();
if ( !winFocus || wxGetTopLevelParent(winFocus) != this ) if ( !winFocus || wxGetTopLevelParent(winFocus) != this )
{ DoRestoreLastFocus();
wxWindow *parent = m_winLastFocused ? m_winLastFocused->GetParent()
: NULL;
if ( !parent )
{
parent = this;
}
wxSetFocusToChild(parent, &m_winLastFocused);
}
} }
else // deactivating else // deactivating
{ {
// remember the last focused child if it is our child DoSaveLastFocus();
m_winLastFocused = FindFocus();
if ( m_winLastFocused )
{
// let it know that it doesn't have focus any more
// But this will already be done via WM_KILLFOCUS, so we'll get two kill
// focus events if we call it explicitly.
// m_winLastFocused->HandleKillFocus((WXHWND)NULL);
// and don't remember it if it's a child from some other frame
if ( wxGetTopLevelParent(m_winLastFocused) != this )
{
m_winLastFocused = NULL;
}
}
wxLogTrace(wxT("focus"), wxLogTrace(wxT("focus"),
wxT("wxTLW %p deactivated, last focused: %p."), wxT("wxTLW %p deactivated, last focused: %p."),