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]; +}