From cb266682041ff76cec245b874ac45e1236d7db18 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 10 Nov 2015 16:09:21 +0100 Subject: [PATCH] Don't destroy the native window in wxNativeWindow itself by default Leave ownership of the native window to the user code as it may want to reuse it for some other purpose and provide an explicit Disown() function that can be called if the user really wants wxWidgets to take ownership of the native window. In particular, this avoids problems when using ARC under OS X which resulted in a double "release" before. --- include/wx/nativewin.h | 38 +++++++++++++++++++++++++++++++++++++- interface/wx/nativewin.h | 28 +++++++++++++++++++++++----- samples/widgets/native.cpp | 32 ++++++++++++++++++++++++++++++++ src/gtk/nativewin.cpp | 11 +++++++---- src/msw/nativewin.cpp | 13 +++++++++++++ src/osx/cocoa/nativewin.mm | 8 ++++++++ 6 files changed, 120 insertions(+), 10 deletions(-) diff --git a/include/wx/nativewin.h b/include/wx/nativewin.h index ae016b5b24..b2ebad0685 100644 --- a/include/wx/nativewin.h +++ b/include/wx/nativewin.h @@ -69,7 +69,10 @@ class WXDLLIMPEXP_CORE wxNativeWindow : public wxWindow { public: // Default ctor, Create() must be called later to really create the window. - wxNativeWindow() { } + wxNativeWindow() + { + Init(); + } // Create a window from an existing native window handle. // @@ -80,13 +83,46 @@ public: // 0 if the handle was invalid. wxNativeWindow(wxWindow* parent, wxWindowID winid, wxNativeWindowHandle handle) { + Init(); + Create(parent, winid, handle); } // Same as non-default ctor, but with a return code. bool Create(wxWindow* parent, wxWindowID winid, wxNativeWindowHandle handle); + // By default the native window with which this wxWindow is associated is + // owned by the user code and needs to be destroyed by it in a platform + // specific way, however this function can be called to let wxNativeWindow + // dtor take care of destroying the native window instead of having to do + // it from the user code. + void Disown() + { + wxCHECK_RET( m_ownedByUser, wxS("Can't disown more than once") ); + + m_ownedByUser = false; + + DoDisown(); + } + +#ifdef __WXMSW__ + // Prevent the native window, not owned by us, from being destroyed by the + // base class dtor, unless Disown() had been called. + virtual ~wxNativeWindow(); +#endif // __WXMSW__ + private: + void Init() + { + m_ownedByUser = true; + } + + // This is implemented in platform-specific code. + void DoDisown(); + + // If the native widget owned by the user code. + bool m_ownedByUser; + wxDECLARE_NO_COPY_CLASS(wxNativeWindow); }; diff --git a/interface/wx/nativewin.h b/interface/wx/nativewin.h index b81770bde5..ab3ece153e 100644 --- a/interface/wx/nativewin.h +++ b/interface/wx/nativewin.h @@ -15,8 +15,8 @@ This class can be used as a bridge between wxWidgets and native GUI toolkit, i.e. standard Windows controls under MSW, GTK+ widgets or Cocoa views. Using it involves writing code specific to each platform, at the - very least for creating the native window, but possibly also to handle its - events, but this class takes care of all the generic parts. + very least for creating and destroying the native window, but possibly also + to handle its events, but this class takes care of all the generic parts. @note Check whether @c wxHAS_NATIVE_WINDOW is defined before using this class as it is not available under all platforms. @@ -26,14 +26,24 @@ @code #include - wxNativeWindow* switch = new wxNativeWindow(parent, wxID_ANY, gtk_switch_new()); + GtkWidget* w = gtk_switch_new(); + wxNativeWindow* switch = new wxNativeWindow(parent, wxID_ANY, w); + g_object_unref(w); @endcode and then use @c switch as usual, e.g. add it to a sizer to layout it correctly. Of course, you will still have to use the native GTK+ functions to handle its events and change or retrieve its state. - See the "native" page of the widgets sample for another example of using - it. + Notice that the native window still remains owned by the caller, to allow + reusing it later independently of wxWidgets. If you want wxWidgets to + delete the native window when the wxNativeWindow itself is destroyed, you + need to explicitly call Disown(). Otherwise you need to perform the + necessary cleanup in your own code by calling the appropriate + platform-specific function: under MSW, this is @c ::DestroyWindow(), under + GTK @c g_object_unref() and under Cocoa -- @c -release:. + + See the "native" page of the widgets sample for the examples of using + this class under all major platforms. @since 3.1.0 @@ -76,4 +86,12 @@ public: typically because the supplied parameters are invalid. */ bool Create(wxWindow* parent, wxWindowID winid, wxNativeWindowHandle handle); + + /** + Indicate that the user code yields ownership of the native window. + + This method can be called at most once and after calling it, the native + window will be destroyed when this wxNativeWindow object is. + */ + void Disown(); }; diff --git a/samples/widgets/native.cpp b/samples/widgets/native.cpp index 46b21e9caa..e9234fe480 100644 --- a/samples/widgets/native.cpp +++ b/samples/widgets/native.cpp @@ -95,6 +95,16 @@ public: (void)Create(parent, wxID_ANY, hwnd); } + virtual ~NativeWindow() + { + // If you don't call this, you need to call DestroyWindow() later. + // + // Also notice that a HWND can't continue to exist under MSW if its + // parent its destroyed, so you may also want to reparent it under some + // other window if the parent of this window is also getting destroyed. + Disown(); + } + protected: // This code requires NMBCDROPDOWN to work, we don't really want to // reproduce its definition here for very old compilers not having it. @@ -146,9 +156,22 @@ public: ); #endif // GTK+ 3.6/earlier + g_object_ref_sink(widget); + (void)Create(parent, wxID_ANY, widget); } + virtual ~NativeWindow() + { + // If you don't call this, you need to call g_object_unref() on the + // widget yourself. The advantage of this is that you don't necessarily + // have to do it right now and could keep using the native widget and + // destroy it later. But if you don't need it any longer, as is the + // case here, it's simpler to just call Disown() to let it be destroyed + // immediately. + Disown(); + } + private: wxMenu m_menu; }; @@ -185,6 +208,15 @@ public: (void)Create(parent, wxID_ANY, v); } + + virtual ~NativeWindow() + { + // If you don't call this, you need to call -release: on the button + // manually (see the comment in wxGTK version above) if using manual + // reference counting. If you build with ARC, you must *not* call + // Disown() to let the native view be destroyed automatically. + Disown(); + } }; #else // some other platform diff --git a/src/gtk/nativewin.cpp b/src/gtk/nativewin.cpp index e0f5ad7a3e..8103ef4346 100644 --- a/src/gtk/nativewin.cpp +++ b/src/gtk/nativewin.cpp @@ -54,11 +54,9 @@ wxNativeWindow::Create(wxWindow* parent, if ( !CreateBase(parent, winid) ) return false; - // Add a reference to the widget to match g_object_unref() in wxWindow dtor - // (and by using the "_sink" version we avoid memory leaks when we're - // passed a newly allocated widget, as is typically the case). + // Add a reference to the widget to match g_object_unref() in wxWindow dtor. m_widget = widget; - g_object_ref_sink(m_widget); + g_object_ref(m_widget); parent->DoAddChild(this); @@ -73,6 +71,11 @@ wxNativeWindow::Create(wxWindow* parent, return true; } +void wxNativeWindow::DoDisown() +{ + g_object_unref(m_widget); +} + // ---------------------------------------------------------------------------- // wxNativeContainerWindow // ---------------------------------------------------------------------------- diff --git a/src/msw/nativewin.cpp b/src/msw/nativewin.cpp index c08d52a350..eb799345f6 100644 --- a/src/msw/nativewin.cpp +++ b/src/msw/nativewin.cpp @@ -79,6 +79,19 @@ wxNativeWindow::Create(wxWindow* parent, return true; } +void wxNativeWindow::DoDisown() +{ + // We don't do anything here, clearing m_ownedByUser flag is enough. +} + +wxNativeWindow::~wxNativeWindow() +{ + // Restore the original window proc and reset HWND to 0 to prevent it from + // being destroyed in the base class dtor if it's owned by user code. + if ( m_ownedByUser ) + UnsubclassWin(); +} + // ---------------------------------------------------------------------------- // wxNativeContainerWindow // ---------------------------------------------------------------------------- diff --git a/src/osx/cocoa/nativewin.mm b/src/osx/cocoa/nativewin.mm index ca3c199a43..f2be5a7873 100644 --- a/src/osx/cocoa/nativewin.mm +++ b/src/osx/cocoa/nativewin.mm @@ -59,6 +59,9 @@ wxNativeWindow::Create(wxWindow* parent, else if ( [view respondsToSelector:@selector(stringValue)] ) m_label = wxCFStringRef::AsString([(id)view stringValue]); + // As wxWidgets will release the view when this object is destroyed, retain + // it here to avoid destroying the view owned by the user code. + [view retain]; SetPeer(new wxWidgetCocoaImpl(this, view)); // It doesn't seem necessary to use MacPostControlCreate() here as we never @@ -66,3 +69,8 @@ wxNativeWindow::Create(wxWindow* parent, return true; } + +void wxNativeWindow::DoDisown() +{ + [GetPeer()->GetWXWidget() release]; +}