Add wxPU_CONTAINS_CONTROLS style for wxPopupWindow

This restores the default behavior of a popup window in MSW to the
behavior it had before 56c4191168. The new
flag added by this commit can be used to give the popup window the
behavior from after that commit, i.e. choose the implementation using a
WS_POPUP window rather than the default one using a WS_CHILD of the
desktop.

The old behavior kept the popup from taking focus from its parent window
but left some controls not working. The new behavior has the popup take
focus and lets all controls work.

Closes https://github.com/wxWidgets/wxWidgets/pull/1123
This commit is contained in:
New Pagodi
2019-01-09 12:09:30 -06:00
committed by Vadim Zeitlin
parent 8deb2cf0bb
commit 07f64c3b75
4 changed files with 100 additions and 8 deletions

View File

@@ -25,11 +25,15 @@ public:
bool Create(wxWindow *parent, int flags = wxBORDER_NONE);
virtual void SetFocus() wxOVERRIDE;
virtual bool Show(bool show = true) wxOVERRIDE;
// return the style to be used for the popup windows
virtual WXDWORD MSWGetStyle(long flags, WXDWORD *exstyle) const wxOVERRIDE;
// get the HWND to be used as parent of this window with CreateWindow()
virtual WXHWND MSWGetParent() const wxOVERRIDE;
// Implementation only from now on.

View File

@@ -15,6 +15,15 @@
#if wxUSE_POPUPWIN
// ----------------------------------------------------------------------------
// wxPopupWindow specific flags
// ----------------------------------------------------------------------------
// This flag can be used in MSW if some controls are not working with the
// default popup style.
#define wxPU_CONTAINS_CONTROLS 0x0001
#include "wx/nonownedwnd.h"
// ----------------------------------------------------------------------------
@@ -124,6 +133,13 @@ public:
wxPopupTransientWindow() { }
wxPopupTransientWindow(wxWindow *parent, int style = wxBORDER_NONE)
{ Create(parent, style); }
bool Create(wxWindow *parent, int style = wxBORDER_NONE)
{
return wxPopupTransientWindowBase::Create
(
parent, style | wxPU_CONTAINS_CONTROLS
);
}
// Implement base class pure virtuals.
virtual void Popup(wxWindow *focus = NULL) wxOVERRIDE;

View File

@@ -11,6 +11,19 @@
A special kind of top level window used for popup menus,
combobox popups and such.
@beginStyleTable
@style{wxPU_CONTAINS_CONTROLS}
By default in wxMSW, a popup window will not take focus from its parent
window. However many standard controls, including common ones such as
wxTextCtrl, need focus to function correctly and will not work when
placed on a default popup. This flag can be used to make the popup take
focus and let all controls work but at the price of not allowing the
parent window to keep focus while the popup is shown, which can also be
sometimes desirable. This style is currently only implemented in MSW
and simply does nothing under the other platforms (it's new since
wxWidgets 3.1.3).
@endStyleTable
@library{wxcore}
@category{managedwnd}

View File

@@ -64,13 +64,24 @@ WXDWORD wxPopupWindow::MSWGetStyle(long flags, WXDWORD *exstyle) const
// we only honour the border flags, the others don't make sense for us
WXDWORD style = wxWindow::MSWGetStyle(flags & wxBORDER_MASK, exstyle);
// We need to be a popup (i.e. not a child) window in order to not be
// confined to the parent window area, as is required for a drop down, for
// example. Old implementation used WS_CHILD and made this window a child
// of the desktop window, but this resulted in problems with handling input
// in the popup children, and so was changed to the current version.
// wxMSW uses 2 rather different implementations of wxPopupWindow
// internally, each one with its own limitations, so we allow specifying
// wxPU_CONTAINS_CONTROLS flag to select which one is used. The default is
// to use a child window of the desktop for the popup, which is compatible
// with the previous wxWidgets versions and works well for simple popups,
// but many standard controls can't work as children of such a window
// because it doesn't accept focus. So an alternative implementation for
// the popups that will contain such controls is available, but this one
// has problems due to the fact that it does take focus and not only can
// (and does) this break existing code, but it also prevents the parent
// window from keeping focus while showing the popup, as must be done when
// using auto-completion tooltips, for example. So neither implementation
// can be used in all cases and you have to explicitly choose your poison.
if ( HasFlag(wxPU_CONTAINS_CONTROLS) )
{
style &= ~WS_CHILD;
style |= WS_POPUP;
}
if ( exstyle )
{
@@ -81,6 +92,31 @@ WXDWORD wxPopupWindow::MSWGetStyle(long flags, WXDWORD *exstyle) const
return style;
}
WXHWND wxPopupWindow::MSWGetParent() const
{
if ( HasFlag(wxPU_CONTAINS_CONTROLS) )
{
return wxPopupWindowBase::MSWGetParent();
}
else
{
// we must be a child of the desktop to be able to extend beyond the
// parent window client area (like the comboboxes drop downs do)
return (WXHWND)::GetDesktopWindow();
}
}
void wxPopupWindow::SetFocus()
{
// Focusing on a popup window does not work on MSW unless WS_POPUP style
// is set. Since this is only the case if the style wxPU_CONTAINS_CONTROLS
// is used, we'll handle the focus in that case and otherwise do nothing.
if ( HasFlag(wxPU_CONTAINS_CONTROLS) )
{
wxPopupWindowBase::SetFocus();
}
}
bool wxPopupWindow::Show(bool show)
{
// It's important to update wxCurrentPopupWindow before showing the window,
@@ -88,7 +124,30 @@ bool wxPopupWindow::Show(bool show)
// from inside Show() so that it knows to remain [appearing] active.
wxCurrentPopupWindow = show ? this : NULL;
if ( HasFlag(wxPU_CONTAINS_CONTROLS) )
{
return wxPopupWindowBase::Show(show);
}
else
{
if ( !wxWindowMSW::Show(show) )
return false;
if ( show )
{
// raise to top of z order
if ( !::SetWindowPos(GetHwnd(), HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE) )
{
wxLogLastError(wxT("SetWindowPos"));
}
// and set it as the foreground window so the mouse can be captured
::SetForegroundWindow(GetHwnd());
}
return true;
}
}
// ----------------------------------------------------------------------------