Project

General

Profile

Actions

Bug #13115

closed

Widgets removed from disabled parents aren't rerendered correctly

Added by Marnik Roosen 2 months ago. Updated about 2 months ago.

Status:
Closed
Priority:
Normal
Assignee:
Target version:
Start date:
10/16/2024
Due date:
% Done:

100%

Estimated time:

Description

When a parent widget is disabled, it will set a disabled flag which propagates to all child widgets.
The child widgets themselves won't get a disabled flag themselves.
They will keep their state to ensure that once the parent widget is enabled, the child widgets will get their previous state back.
Concretely:

  1. Create a view with some items
  2. Disable item 2
  3. Disable the view
  4. Enable the view
  5. Item two is still disabled

Say we were to remove an enabled child widget from a disabled parent widget, and add that child widget to the DOM tree again.
That child widget would still appear disabled. Even if we would manually call childWidget->enable(), the widget remains disabled.

This issue is caused by the optimization within the following method. The child widget itself wasn't disabled, so it won't trigger a rerender.

void WWebWidget::setDisabled(bool disabled)
{
  if (canOptimizeUpdates() && (disabled == flags_.test(BIT_DISABLED)))
    return;

  ...
}

We should actually avoid this optimization when the parent widget changed, since the disabled state depends on the parent widget.

Test case to reproduce the issue:

#include <Wt/WApplication.h>
#include <Wt/WEnvironment.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WPushButton.h>
#include <Wt/WServer.h>

class ExtractDisabledChildApplication : public Wt::WApplication
{
public:
  ExtractDisabledChildApplication(const Wt::WEnvironment& env)
    : Wt::WApplication(env)
  {
    auto container = root()->addNew<Wt::WContainerWidget>();
    auto text = container->addNew<Wt::WText>("I'm disabled due to the parent widget");

    container->disable();

    auto button = root()->addNew<Wt::WPushButton>("Click me!");
    button->clicked().connect([this, button, container, text] {
      button->disable();

      auto w = container->removeWidget(text);
      w->enable(); // This doesn't have any effect
      w->setText("I should be enabled now, since I'm part of an enabled root widget and I'm not disabled myself");
      root()->addWidget(std::move(w));
    });
  }
};

int main(int argc, char *argv[])
{
  return Wt::WRun(argc, argv, [](const Wt::WEnvironment &env) {
    return std::make_unique<ExtractDisabledChildApplication>(env);
  });
}
Actions #1

Updated by Marnik Roosen 2 months ago

  • Description updated (diff)
Actions #2

Updated by Marnik Roosen 2 months ago

  • Status changed from New to InProgress
  • Assignee set to Marnik Roosen
Actions #3

Updated by Marnik Roosen 2 months ago

  • Description updated (diff)
Actions #4

Updated by Marnik Roosen 2 months ago

  • Status changed from InProgress to Review
  • Assignee deleted (Marnik Roosen)
  • Target version set to 4.11.1
Actions #5

Updated by Matthias Van Ceulebroeck 2 months ago

  • Assignee set to Matthias Van Ceulebroeck
Actions #6

Updated by Matthias Van Ceulebroeck 2 months ago

  • Status changed from Review to Implemented @Emweb
  • Assignee changed from Matthias Van Ceulebroeck to Marnik Roosen
  • % Done changed from 0 to 100
Actions #7

Updated by Matthias Van Ceulebroeck 2 months ago

  • Status changed from Implemented @Emweb to Implemented @Test
Actions #8

Updated by Matthias Van Ceulebroeck about 2 months ago

  • Status changed from Implemented @Test to Closed
Actions

Also available in: Atom PDF