Project

General

Profile

Actions

Bug #7669

closed

Some examples with WSuggestionPopup produce duplicate DOM ids and exhibit incorrect behavior

Added by Bruce Toll almost 4 years ago. Updated about 3 years ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-
Start date:
07/28/2020
Due date:
% Done:

0%

Estimated time:

Description

This issue can be observed in the composer and planner examples with github wt 4.4.0-rc1-1-gea5f938d --- although I don't believe it is a recent regression. For the composer, click on "Add Cc", then type a "b". A suggestion box will appear with one email address. However, it will not match additional characters or handle backspaces as would be expected. It will accept enter, but only once, and then the suggestion box will not reappear. The planner exhibits similar behavior for the time fields.

The issue appears to be due to the use of addWidget() rather than addChild() for the WSuggestionPopup subclasses in these examples. Since these classes are derived from WPopupWidget, the use of addWidget() causes them to be added to the DOM twice, once via createStubElement() and a second time via addGlobalWidget(). The duplicate ids can cause problems for the JavaScript code (handling the keyup event), as seen in the examples.

There is a similar issue with the widgetgallery's Autocomplete example, where addNew() is used to add a WSuggestionPopup. This example appears to function correctly, although it also generates a duplicate id. The difference in behavior is likely due to the ordering of the duplicate elements.

I originally encountered this issue when coding a test program to include with #7666. I made the mistake of using addNew() for a WSuggestionPopup and spent some time debugging the error. Since it is always an error (?) to use addWidget with a subclass of WPopupWidget, I tried adding some static_asserts to Wt to catch this case. That's what revealed the problems with the three examples.

Attached for your review are two patches that have been lightly tested. The first is intended to fix the planner and composer examples. It also adds some static_asserts based on the assumption that it is an error to call the addWidget() related functions with an instance derived from WPopupWidget. The second patch is intended to fix the Autocomplete example and adds support for a addChildNew() method, as well as an additional static_assert. There are more comments on the patches.


Files


Related issues 1 (1 open0 closed)

Related to Feature #8620: Consider improvements to the API to prevent bugs like issue #7669New06/02/2021

Actions
Actions #1

Updated by Bruce Toll almost 4 years ago

See also #7574 which may be related...

Actions #2

Updated by Roel Standaert almost 4 years ago

It seems to me that the static assert is right. The addChildNew is kind of an awkward name, addNewChild makes a bit more sense.

I think I'll add the fixes to the examples to Wt 4.4.0, but keep the static assert and addNewChild for the next release.

Actions #3

Updated by Roel Standaert almost 4 years ago

  • Target version set to 4.5.0
Actions #4

Updated by Roel Standaert almost 4 years ago

Another (maybe slightly crazy) idea would be to use type traits and enable_if to select the implementation of addNew to use:

  1. addNew for a WObject that is not a WWidget: uses addChild
  2. addNew for a WWidget that is not a WPopupWidget: uses addWidget
  3. addNew for a WPopupWidget: uses addChild
Actions #5

Updated by Bruce Toll almost 4 years ago

Thanks for the quick follow-up. Your idea for enhancing addNew() could actually prevent these subtle bugs in user code, rather than just having the compiler exit with an error.

Actions #6

Updated by Roel Standaert over 3 years ago

  • Description updated (diff)
  • Status changed from New to Resolved

I ended up going with a simpler at-runtime approach: any global widget (a widget is automatically flagged as global when WApplication::addGlobalWidget is called) only has its ownership transferred (addChild) instead of being added as a child in the widget tree (addWidget).

I decided against adding addNew to WObject, since it would have limited use in the end (most non-widget WObjects are managed as shared ptrs anyway), and might just confuse users when using addNew on some other widget than WContainerWidget. The main use is really with WPopupWidget and the like.

Actions #7

Updated by Bruce Toll over 3 years ago

Thanks for the upgrade to addNew(). I'm sure that it will prevent a lot of usage errors.

I did notice that some of the related member functions, e.g. addWidget, insertWidget, and insertBefore have not been updated and wanted to double-check that this was intentional.

For clarity, I've attached a patch that complements your new containerwidget_addnew test with tests for addWidget, insertWidget, and insertBefore. These tests do not currently pass and I'm not sure which, if any, should.

For consistency with addNew, it might make sense for addWidget to call addChild if called with a global widget (similar to the logic you added to addNew).

It's a subtle distinction, but I suspect that passing a global widget directly to insertWidget or insertBefore probably indicates a programming error that should be flagged (statically or with an exception?), since the user is being more explicit about what they are trying to accomplish and it doesn't make sense. You can't insert an item before another specified item in a different list.

In any case, the existing commits are a big step forward and very much appreciated!

Actions #8

Updated by Roel Standaert over 3 years ago

Yeah, I might rather indicate to the user that that's not appropriate for global widgets.

Actions #9

Updated by Bruce Toll over 3 years ago

I totally missed this, but another thing to consider is the behavior of WContainerWidget::removeWidget().

There could be existing applications that previously called WContainerWidget::addNew() incorrectly with a global widget and subsequently called removeWidget(). With the new behavior of WContainerWidget::addNew(), the widget will no longer be added to the container, so I believe the subsequent removeWidget() will log an error -- I haven't actually tested it.

If WContainerWidget::addWidget() is modified to have the same behavior as addNew(), it would share the same issue.

I'm not sure of the best way to address this, but thought I'd at least raise the issue for your consideration.

Actions #10

Updated by Roel Standaert over 3 years ago

  • Status changed from Resolved to Feedback

How about a remove() that does a removeWidget(), and if that returns nullptr, it does removeChild()?

Actions #11

Updated by Bruce Toll over 3 years ago

Taking your idea one step further, I wonder if WContainerWidget::removeWidget() itself could be modified to use the new isGlobalWidget() test and invoke removeChild() for global widgets. That way, existing code could benefit, as with your recent update to addNew(). Moreover, there would still be an error logged if removeWidget() was incorrectly called with a non-global object that was not in the container ("removeWidget(): widget not in container").

One concern, though, is maintaining consistent behavior across the many contexts where removeWidget() currently exists, e.g. WStackedWidget, WCompositeWidget, WLayout, etc. Perhaps, it would make things simpler to throw an exception on attempting to add a global widget to any container-like objects other than WContainerWidget? There is no need to special case removeWidget for global widgets, if they can't be added.

Summarizing:

  1. In general, it should not be possible to add global widgets to container-like widgets. Attempts to do so will result in an exception.
  2. As a narrow exception to the above (to maintain compatibility with existing code and simplify a common use case), the addNew(), addwidget(), and removeWidget() methods of WContainerWidget will accept global widgets without error and treat them as child objects.

What do you think?

Actions #12

Updated by Roel Standaert over 3 years ago

  • Target version changed from 4.5.0 to 4.6.0
Actions #13

Updated by Roel Standaert about 3 years ago

  • Status changed from Feedback to Closed
  • Target version deleted (4.6.0)

I'm closing this since the issue was fixed in an old release already. Creating a new issue for further future consideration.

Actions #14

Updated by Roel Standaert about 3 years ago

  • Related to Feature #8620: Consider improvements to the API to prevent bugs like issue #7669 added
Actions

Also available in: Atom PDF