From 0f8618459677e35f91e614a3c9c3f2dc3bc21e99 Mon Sep 17 00:00:00 2001
From: Bruce Toll <btoll@dhsus.com>
Date: Tue, 28 Jul 2020 08:54:25 -0400
Subject: [PATCH 1/2] Fix WPopupWidget use in examples with addWidget

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

That is, a WPopupWidget gets added to the DOM via addGlobalWidget(),
so calling addWidget() adds it a second time as a stub element via
createStubElement().

These duplicate ids, in turn, may cause other problems, e.g. for
the javascript implementation of WSuggestionPopup.

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.

This commmit includes the  addChild() fix for the composer and
planner examples which use subclasses of WSuggestionPopup (that
derives from WPopupWidget). This corrects the issue where the
WSuggestionPopup stops responding to keyboard input.

This commit also adds some static_asserts to catch attempts to call
addWidget(), insertWidget(), or insertBefore() on instances that are
derived from WPopupWidget.
---
 examples/composer/Composer.C   |  2 +-
 examples/planner/EntryDialog.C |  2 +-
 src/Wt/WContainerWidget.h      | 10 ++++++++++
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/examples/composer/Composer.C b/examples/composer/Composer.C
index 8a095b1f..bfaae5f1 100644
--- a/examples/composer/Composer.C
+++ b/examples/composer/Composer.C
@@ -143,7 +143,7 @@ void Composer::createUi()
   /*
    * Addressbook suggestions popup
    */
-  contactSuggestions_ = layout_->addWidget(cpp14::make_unique<ContactSuggestions>());
+  contactSuggestions_ = layout_->addChild(cpp14::make_unique<ContactSuggestions>());
 
   contactSuggestions_->forEdit(toEdit_);
   contactSuggestions_->forEdit(ccEdit_);
diff --git a/examples/planner/EntryDialog.C b/examples/planner/EntryDialog.C
index 7b925ca7..39ccfa88 100644
--- a/examples/planner/EntryDialog.C
+++ b/examples/planner/EntryDialog.C
@@ -47,7 +47,7 @@ EntryDialog::EntryDialog(const WString& title, CalendarCell* cell)
   auto descriptionPtr = cpp14::make_unique<WTextArea>();
   description_ = t->bindWidget("description", std::move(descriptionPtr));
 		
-  TimeSuggestions* suggestions = contents()->addWidget(cpp14::make_unique<TimeSuggestions>());
+  TimeSuggestions* suggestions = contents()->addChild(cpp14::make_unique<TimeSuggestions>());
   suggestions->forEdit(start_);
   suggestions->forEdit(stop_);
 
diff --git a/src/Wt/WContainerWidget.h b/src/Wt/WContainerWidget.h
index 9a7b5c50..467abebf 100644
--- a/src/Wt/WContainerWidget.h
+++ b/src/Wt/WContainerWidget.h
@@ -22,6 +22,7 @@ enum class Overflow {
 
 class WApplication;
 class StdLayoutImpl;
+class WPopupWidget;
 
 /*! \class WContainerWidget Wt/WContainerWidget.h Wt/WContainerWidget.h
  *  \brief A widget that holds and manages child widgets.
@@ -222,6 +223,9 @@ public:
     Widget *addWidget(std::unique_ptr<Widget> widget)
 #ifndef WT_TARGET_JAVA
   {
+    static_assert(!std::is_convertible<Widget*, WPopupWidget*>::value,
+                  "Cannot use addWidget() for types derived from WPopupWidget. "
+                  "Did you mean to use addChild()?");
     Widget *result = widget.get();
     addWidget(std::unique_ptr<WWidget>(std::move(widget)));
     return result;
@@ -301,6 +305,9 @@ public:
     Widget *insertBefore(std::unique_ptr<Widget> widget, WWidget *before)
 #ifndef WT_TARGET_JAVA
   {
+    static_assert(!std::is_convertible<Widget*, WPopupWidget*>::value,
+                  "Cannot use insertBefore() for types derived from WPopupWidget. "
+                  "Did you mean to use addChild()?");
     Widget *result = widget.get();
     insertBefore(std::unique_ptr<WWidget>(std::move(widget)), before);
     return result;
@@ -336,6 +343,9 @@ public:
     Widget *insertWidget(int index, std::unique_ptr<Widget> widget)
 #ifndef WT_TARGET_JAVA
   {
+    static_assert(!std::is_convertible<Widget*, WPopupWidget*>::value,
+                  "Cannot use insertWidget() for types derived from WPopupWidget. "
+                  "Did you mean to use addChild()?");
     Widget *result = widget.get();
     insertWidget(index, std::unique_ptr<WWidget>(std::move(widget)));
     return result;
-- 
2.23.2

