From ce92055f3a39a20a37a44cbd753a1c0710a01f99 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 23 Aug 2021 17:53:03 +0100 Subject: [PATCH] Fix the effective parent of wxGenericFindReplaceDialog This dialog is non-modal and so using GetParentForModalDialog() for it is not quite right, because this function checks that the candidate parent window is visible, which may not be the case for a modeless dialog parent, as the dialog isn't necessarily going to be shown immediately, but may well be shown later, after showing its parent. And not allowing to use the not yet shown parent was also inconsistent with the native MSW version which didn't have any problem with this. So fix this by adding new GetParentForModelessDialog() function and using it for this modeless dialog instead. This required slightly refactoring wxDialog code to allow reusing most of it between the old GetParentForModalDialog() and the new function. --- include/wx/dialog.h | 27 +++++++++++++++++++++++---- src/common/dlgcmn.cpp | 31 +++++++++++++++++++++++-------- src/generic/fdrepdlg.cpp | 2 +- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/include/wx/dialog.h b/include/wx/dialog.h index 55dc6d0fe2..32db8b5b90 100644 --- a/include/wx/dialog.h +++ b/include/wx/dialog.h @@ -105,7 +105,10 @@ public: // check whether it contains wxDIALOG_NO_PARENT bit. // // This function always returns a valid top level window or NULL. - wxWindow *GetParentForModalDialog(wxWindow *parent, long style) const; + wxWindow *GetParentForModalDialog(wxWindow *parent, long style) const + { + return DoGetParentForDialog(wxDIALOG_MODALITY_APP_MODAL, parent, style); + } // This overload can only be used for already initialized windows, i.e. not // from the ctor. It uses the current window parent and style. @@ -114,6 +117,16 @@ public: return GetParentForModalDialog(GetParent(), GetWindowStyle()); } + // This function is similar to GetParentForModalDialog() but should be used + // for modeless dialogs and skips the checks irrelevant for them (currently + // just the one checking that the candidate parent window is visible, as it + // is possible to create a modeless dialog before its parent is shown if it + // is only shown later, after showing the parent). + wxWindow *GetParentForModelessDialog(wxWindow *parent, long style) const + { + return DoGetParentForDialog(wxDIALOG_MODALITY_NONE, parent, style); + } + #if wxUSE_STATTEXT // && wxUSE_TEXTCTRL // splits text up at newlines and places the lines into a vertical // wxBoxSizer, with the given maximum width, lines will not be wrapped @@ -243,9 +256,15 @@ protected: static bool sm_layoutAdaptation; private: - // helper of GetParentForModalDialog(): returns the passed in window if it - // can be used as our parent or NULL if it can't - wxWindow *CheckIfCanBeUsedAsParent(wxWindow *parent) const; + // Common implementation of GetParentFor{Modal,Modeless}Dialog(). + wxWindow *DoGetParentForDialog(wxDialogModality modality, + wxWindow *parent, + long style) const; + + // helper of DoGetParentForDialog(): returns the passed in window if it + // can be used as parent for this kind of dialog or NULL if it can't + wxWindow *CheckIfCanBeUsedAsParent(wxDialogModality modality, + wxWindow *parent) const; // Helper of OnCharHook() and OnCloseWindow(): find the appropriate button // for closing the dialog and send a click event for it. diff --git a/src/common/dlgcmn.cpp b/src/common/dlgcmn.cpp index dfc23046ca..52b660ab09 100644 --- a/src/common/dlgcmn.cpp +++ b/src/common/dlgcmn.cpp @@ -128,7 +128,9 @@ wxDialogBase::wxDialogBase() SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS); } -wxWindow *wxDialogBase::CheckIfCanBeUsedAsParent(wxWindow *parent) const +wxWindow * +wxDialogBase::CheckIfCanBeUsedAsParent(wxDialogModality modality, + wxWindow *parent) const { if ( !parent ) return NULL; @@ -148,10 +150,21 @@ wxWindow *wxDialogBase::CheckIfCanBeUsedAsParent(wxWindow *parent) const return NULL; } - if ( !parent->IsShownOnScreen() ) + // This check is done for modal dialogs only because modeless dialogs can + // be created before their parent is shown and only shown later. + switch ( modality ) { - // using hidden parent won't work correctly neither - return NULL; + case wxDIALOG_MODALITY_NONE: + break; + + case wxDIALOG_MODALITY_APP_MODAL: + case wxDIALOG_MODALITY_WINDOW_MODAL: + if ( !parent->IsShownOnScreen() ) + { + // using hidden parent won't work correctly neither + return NULL; + } + break; } if ( parent == this ) @@ -165,7 +178,9 @@ wxWindow *wxDialogBase::CheckIfCanBeUsedAsParent(wxWindow *parent) const } wxWindow * -wxDialogBase::GetParentForModalDialog(wxWindow *parent, long style) const +wxDialogBase::DoGetParentForDialog(wxDialogModality modality, + wxWindow *parent, + long style) const { // creating a parent-less modal dialog will result (under e.g. wxGTK2) // in an unfocused dialog, so try to find a valid parent for it unless we @@ -175,16 +190,16 @@ wxDialogBase::GetParentForModalDialog(wxWindow *parent, long style) const // first try the given parent if ( parent ) - parent = CheckIfCanBeUsedAsParent(wxGetTopLevelParent(parent)); + parent = CheckIfCanBeUsedAsParent(modality, wxGetTopLevelParent(parent)); // then the currently active window if ( !parent ) - parent = CheckIfCanBeUsedAsParent( + parent = CheckIfCanBeUsedAsParent(modality, wxGetTopLevelParent(wxGetActiveWindow())); // and finally the application main window if ( !parent ) - parent = CheckIfCanBeUsedAsParent(wxApp::GetMainTopWindow()); + parent = CheckIfCanBeUsedAsParent(modality, wxApp::GetMainTopWindow()); return parent; } diff --git a/src/generic/fdrepdlg.cpp b/src/generic/fdrepdlg.cpp index eec427ee32..c2c169cb8a 100644 --- a/src/generic/fdrepdlg.cpp +++ b/src/generic/fdrepdlg.cpp @@ -83,7 +83,7 @@ bool wxGenericFindReplaceDialog::Create(wxWindow *parent, const wxString& title, int style) { - parent = GetParentForModalDialog(parent, style); + parent = GetParentForModelessDialog(parent, style); if ( !wxDialog::Create(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize,