From 0c6dff03708fdc2d310f412cbc3cb7b705bc943e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 5 Feb 2012 14:18:28 +0000 Subject: [PATCH] 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 --- include/wx/msw/toplevel.h | 11 +++- src/msw/toplevel.cpp | 117 +++++++++++++++++++++++++------------- 2 files changed, 88 insertions(+), 40 deletions(-) diff --git a/include/wx/msw/toplevel.h b/include/wx/msw/toplevel.h index e0a7453b68..fdc62eba5e 100644 --- a/include/wx/msw/toplevel.h +++ b/include/wx/msw/toplevel.h @@ -181,7 +181,16 @@ protected: bool m_fsIsMaximized; 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; #if defined(__SMARTPHONE__) && defined(__WXWINCE__) diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 51972423ad..1db870f1e8 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -361,13 +361,6 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX #endif // __SMARTPHONE__ || __POCKETPC__ 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: // @@ -378,15 +371,48 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX // using the bitwise AND operator. unsigned id = wParam & 0xfff0; - // SC_SIZE is the first of the system-defined commands and we - // leave those to DefWindowProc(). - if ( id < SC_SIZE ) + // Preserve the focus when minimizing/restoring the window: we + // need to do it manually as DefWindowProc() doesn't appear to + // 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) ) processed = true; } - } #endif // #ifndef __WXUNIVERSAL__ + } break; } @@ -1338,47 +1364,60 @@ void wxTopLevelWindowMSW::DoThaw() // wxTopLevelWindow event handling // ---------------------------------------------------------------------------- -// Default activation behaviour - set the focus for the first child -// subwindow found. +void wxTopLevelWindowMSW::DoSaveLastFocus() +{ + 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) { 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 // have it wxLogTrace(wxT("focus"), wxT("wxTLW %p activated."), m_hWnd); wxWindow *winFocus = FindFocus(); if ( !winFocus || wxGetTopLevelParent(winFocus) != this ) - { - wxWindow *parent = m_winLastFocused ? m_winLastFocused->GetParent() - : NULL; - if ( !parent ) - { - parent = this; - } - - wxSetFocusToChild(parent, &m_winLastFocused); - } + DoRestoreLastFocus(); } else // deactivating { - // remember the last focused child if it is our child - 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; - } - } + DoSaveLastFocus(); wxLogTrace(wxT("focus"), wxT("wxTLW %p deactivated, last focused: %p."), -- 2.45.2