From ea3022e2cd9e641713b5dbc81f424f53f2845356 Mon Sep 17 00:00:00 2001
From: Bruce Toll <btoll@dhsus.com>
Date: Tue, 28 Jul 2020 09:49:07 -0400
Subject: [PATCH 2/2] Fix WPopupWidget use in autocomplete example

The use of addNew() for instances derived from WPopupWidget is
incorrect as it leads to duplicate element ids in the DOM.

Instead, addChild() should be called to set ownership for the
WPopupWidget so that it remains available as long as needed, but
eventually gets cleaned-up along with the owning WObject.

As a convenience, this commit also adds an addChildNew() method which
can be used as a drop-in replacement for addNew() for instances of
WPopupWidget and derived classes where it is only intended that an
object be constructed and ownership transferred to a parent WObject.

This commmit includes the  addChildNew() fix for the WidgetGallery's
autocomplete example. NOTE: This example functioned OK prior to the
fix, but it was still incorrectly adding duplicate ids to the DOM.

This commit also adds a static_asserts to catch attempts to call
addNew() with types derived from WPopupWidget.
---
 .../widgetgallery/examples/AutoComplete.cpp   |  2 +-
 src/Wt/WContainerWidget.h                     |  3 +++
 src/Wt/WObject.h                              | 27 +++++++++++++++++++
 3 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/examples/widgetgallery/examples/AutoComplete.cpp b/examples/widgetgallery/examples/AutoComplete.cpp
index 69609677..555a1e4b 100644
--- a/examples/widgetgallery/examples/AutoComplete.cpp
+++ b/examples/widgetgallery/examples/AutoComplete.cpp
@@ -16,7 +16,7 @@ contactOptions.wordSeparators = "-., \"@\n;";
 contactOptions.appendReplacedText = ", ";
 
 Wt::WSuggestionPopup *sp =
-    container->addNew<Wt::WSuggestionPopup>(
+    container->addChildNew<Wt::WSuggestionPopup>(
             Wt::WSuggestionPopup::generateMatcherJS(contactOptions),
             Wt::WSuggestionPopup::generateReplacerJS(contactOptions));
 
diff --git a/src/Wt/WContainerWidget.h b/src/Wt/WContainerWidget.h
index 467abebf..d764841d 100644
--- a/src/Wt/WContainerWidget.h
+++ b/src/Wt/WContainerWidget.h
@@ -257,6 +257,9 @@ public:
   template <typename Widget, typename ...Args>
     Widget *addNew( Args&& ...args )
   {
+    static_assert(!std::is_convertible<Widget*, WPopupWidget*>::value,
+                  "Cannot use addNew() for types derived from WPopupWidget. "
+                  "Did you mean to use addChildNew()?");
     std::unique_ptr<Widget> w{new Widget(std::forward<Args>(args)...)};
     Widget *result = w.get();
     addWidget(std::unique_ptr<WWidget>{std::move(w)});
diff --git a/src/Wt/WObject.h b/src/Wt/WObject.h
index 3a9558f5..fc053812 100644
--- a/src/Wt/WObject.h
+++ b/src/Wt/WObject.h
@@ -113,6 +113,33 @@ public:
     return result;
   }
 
+  /*! \brief Create a child WObject and add it, returning a raw pointer.
+   *
+   * This is implemented as:
+   *
+   * \code
+   * std::unique_ptr<Child> child{new Child(std::forward<Args>(args)...)};
+   * Child *result = child.get();
+   * addChild(std::unique_ptr<WObject>(std::move(child)));
+   * return result;
+   * \endcode
+   *
+   * This is a useful shorthand for adding a new child object, and
+   * getting a reference to it, e.g.:
+   *
+   * \code
+   * Wt::WMessageBox *mb = addChildNew<Wt::WMessageBox>("Status", "All is well...", Wt::Icon::Information, Wt::StandardButton::Ok);
+   * \endcode
+   */
+  template <typename Child, typename ...Args>
+    Child *addChildNew( Args&& ...args )
+  {
+    std::unique_ptr<Child> child{new Child(std::forward<Args>(args)...)};
+    Child *result = child.get();
+    addChild(std::unique_ptr<WObject>(std::move(child)));
+    return result;
+  }
+
   /*! \brief Remove a child WObject, so its lifetime is no longer determined by this WObject
    */
   std::unique_ptr<WObject> removeChild(WObject *child);
-- 
2.23.2

