Fix using wxMemoryDC without a GUI wxApp instance

This used to work, at least in wxMSW, but stopped working after the
(perfectly valid, on their own) changes of 2508efdd6e (Initialize
wxMemoryDC with a default font, 2019-08-13), as this resulted in calling
wxApp::GetTopWindow() that can only be called from the GUI code.

Fix this by adding wxApp::GetGUIInstance() and using it in
GetMainTopWindow(), so that we only call GetTopWindow() if we actually
have a GUI wxApp object on which to call it.

Implement this in terms of a new virtual IsGUI() which seems slightly
better than, although roughly equivalent to, using wxDynamicCast().

Closes https://github.com/wxWidgets/wxWidgets/pull/2617

Closes #19343.
This commit is contained in:
Vadim Zeitlin
2021-12-12 17:05:58 +00:00
parent b9a1931394
commit e3ec9fb124
3 changed files with 51 additions and 4 deletions

View File

@@ -462,6 +462,9 @@ public:
static wxAppConsole *GetInstance() { return ms_appInstance; } static wxAppConsole *GetInstance() { return ms_appInstance; }
static void SetInstance(wxAppConsole *app) { ms_appInstance = app; } static void SetInstance(wxAppConsole *app) { ms_appInstance = app; }
// returns true for GUI wxApp subclasses
virtual bool IsGUI() const { return false; }
// command line arguments (public for backwards compatibility) // command line arguments (public for backwards compatibility)
int argc; int argc;
@@ -684,6 +687,19 @@ public:
// deactivated // deactivated
virtual void SetActive(bool isActive, wxWindow *lastFocus); virtual void SetActive(bool isActive, wxWindow *lastFocus);
virtual bool IsGUI() const wxOVERRIDE { return true; }
// returns non-null pointer only if we have a GUI application object: this
// is only useful in the rare cases when the same code can be used in both
// console and GUI applications, but needs to use GUI-specific functions if
// the GUI is available
static wxAppBase *GetGUIInstance()
{
return ms_appInstance && ms_appInstance->IsGUI()
? static_cast<wxAppBase*>(ms_appInstance)
: NULL;
}
protected: protected:
// override base class method to use GUI traits // override base class method to use GUI traits
virtual wxAppTraits *CreateTraits() wxOVERRIDE; virtual wxAppTraits *CreateTraits() wxOVERRIDE;
@@ -761,7 +777,7 @@ protected:
// return the object of the correct type (i.e. MyApp and not wxApp) // return the object of the correct type (i.e. MyApp and not wxApp)
// //
// the cast is safe as in GUI build we only use wxApp, not wxAppConsole, and in // the cast is safe as in GUI build we only use wxApp, not wxAppConsole, and in
// console mode it does nothing at all // console mode it does nothing at all (but see also wxApp::GetGUIInstance())
#define wxTheApp static_cast<wxApp*>(wxApp::GetInstance()) #define wxTheApp static_cast<wxApp*>(wxApp::GetInstance())
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@@ -251,7 +251,7 @@ public:
Returns the one and only global application object. Returns the one and only global application object.
Usually ::wxTheApp is used instead. Usually ::wxTheApp is used instead.
@see SetInstance() @see SetInstance(), wxApp::GetGUIInstance()
*/ */
static wxAppConsole* GetInstance(); static wxAppConsole* GetInstance();
@@ -838,6 +838,35 @@ public:
*/ */
virtual wxVideoMode GetDisplayMode() const; virtual wxVideoMode GetDisplayMode() const;
/**
Returns the current GUI wxApp object if any or @NULL otherwise.
This function should only be used in the rare cases when the same code
needs to work in both console and GUI applications, but needs to use
GUI-specific functionality if it is available, and so just calling
wxAppConsole::GetInstance() is insufficient while using ::wxTheApp is
incorrect, as the application object is not always a GUI wxApp.
For example:
@code
WXWidget handle = 0;
if ( wxApp* const app = wxApp::GetGUIInstance() ) {
if ( wxWindow* const w = app->GetTopWindow() ) {
handle = w->GetHandle();
}
}
//else: no window to use
some_native_function_taking_a_window_handle(handle);
@endcode
Note that in this particular example, you could use GetMainTopWindow()
which already does the same thing instead of doing it yourself.
@since 3.1.6
*/
static wxAppConsole* GetGUIInstance();
/** /**
Returns @true if the application will exit when the top-level frame is deleted. Returns @true if the application will exit when the top-level frame is deleted.
@@ -1213,7 +1242,9 @@ public:
/** /**
The global pointer to the singleton wxApp object. The global pointer to the singleton wxApp object.
@see wxApp::GetInstance() This pointer can only be used in the GUI applications.
@see wxAppConsole::GetInstance(), wxApp::GetGUIInstance()
*/ */
wxApp *wxTheApp; wxApp *wxTheApp;

View File

@@ -193,7 +193,7 @@ wxWindow* wxAppBase::GetTopWindow() const
/* static */ /* static */
wxWindow* wxAppBase::GetMainTopWindow() wxWindow* wxAppBase::GetMainTopWindow()
{ {
const wxAppBase* const app = static_cast<wxAppBase*>(GetInstance()); const wxAppBase* const app = GetGUIInstance();
return app ? app->GetTopWindow() : NULL; return app ? app->GetTopWindow() : NULL;
} }