Project

General

Profile

Actions

Bug #13861

open

WTabWidget does not size contents correctly when inserted with ContentLoading::Lazy

Added by Stefan Bn 3 months ago. Updated about 4 hours ago.

Status:
Implemented @Emweb
Priority:
Normal
Target version:
Start date:
07/24/2025
Due date:
% Done:

100%

Estimated time:

Description

This behavior started after Wt 4.11.1 and is present in current version Wt 4.12.0. I use many WTabWidgets that are stretched to maximum size in layouts and this issue breakes my entire GUI.

If a tab is added with ContentLoading::Lazy the contents aren't rendered to full size of the surrounding WTabWidget upon first selection (see attached image). Switching between tabs or resizing the browser window leads to a refresh to the correct size.

This refers to current version Wt 4.12.0 on Windows 10; I see this behavior in Firefox and Edge. If compiled with Wt 4.11.1 or earlier this issue is not there.

Sample code that shows the problem:

#include <Wt/WApplication.h>
#include <Wt/WBootstrap5Theme.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WVBoxLayout.h>
#include <Wt/WText.h>
#include <Wt/WTabWidget.h>
#include <Wt/WPanel.h>

using namespace Wt;

std::unique_ptr<WPanel> createDummyPanel(WString title)
{
    auto panel = std::make_unique<WPanel>();

    auto cont = panel->setCentralWidget(std::make_unique<WContainerWidget>());
    cont->decorationStyle().setBackgroundColor(WColor("lightblue"));
    cont->addNew<WText>(title);

    return panel;
}

std::unique_ptr<WApplication> createHelloApplication(const WEnvironment &env)
{
    auto app = std::make_unique<WApplication>(env);

    auto theme = std::make_shared<WBootstrap5Theme>();
    app->setTheme(theme);

    auto vl = app->root()->setLayout(std::make_unique<WVBoxLayout>());

    auto tabs = vl->addWidget(std::make_unique<WTabWidget>(), 1);

    tabs->addTab(createDummyPanel("Content 1 - Should be Full-Size"), "Tab 1", ContentLoading::Lazy);

    // Problem:
    // Tab2 isn't rendered full-size on first selection, due to ContentLoading::Lazy
    // Tab3 with ContentLoading::Eager is okay
    tabs->addTab(createDummyPanel("Content 2 - Should be Full-Size"), "Tab 2", ContentLoading::Lazy);
    tabs->addTab(createDummyPanel("Content 3 - Should be Full-Size"), "Tab 3", ContentLoading::Eager);

    vl->addWidget(std::make_unique<WText>("Bottom"));

    return app;
}

int main(int argc, char *argv[])
{
    return Wt::WRun(argc, argv, &createHelloApplication);
}

Files

TabSizeProblem.JPG (21.2 KB) TabSizeProblem.JPG Stefan Bn, 07/24/2025 01:31 PM
Actions #1

Updated by Stefan Bn 3 months ago

In addition: I see the same behavior when a WMenu is used togehter with a WStackedWidget. When the stacked contents are added using deferCreate, then the size of the content with strechted layout is not shown full size upon first selection. Resizing the browser window only a little bit refreshs the view and the widgets jump to correct size.

auto dummyMenuItem = menu->addItem("Dummy Item", deferCreate(std::bind(&DumyView::createView, this)));
Actions #2

Updated by Stefan Bn about 2 months ago

I did a little more research and I'm wondering why nobody else notices this. It's dramatic since it concerns the initial sizing of all WLayout that are inserted using ContentLoading::Lazy or deferCreate.

This behavior started in Wt 4.11.4 (previous version 4.11.3 is okay, but had the browser-freeze issue). So I assume this is somehow related to this issue/solution:

StdGridLayoutImpl2.js: ensure resizing stretchable items does not loop
https://redmine.emweb.be/issues/13685

Actions #3

Updated by Stefan Bn about 2 months ago

I did a comparison of Wt 4.11.3 and 4.11.4:

https://github.com/emweb/wt/compare/4.11.3...4.11.4

There are changes in src/Wt/WStackedWidget.C and src/Wt/WMenu.C regarding the conditions for the events currentWidgetChanged, e.g.:

 // Only emit if the item has changed, or if emitting has not yet happened.
  if ((hasChanged || !hasEmittedChanged_) && currentIndex_ >= 0) {
    if (loadPolicies_[currentIndex_] == ContentLoading::Lazy) {
      // Lazy loaded widgets are wrapped in a WContainerWidget
      WContainerWidget* container = dynamic_cast<WContainerWidget*>(currentWidget());
      if (container->count() > 0) {
        currentWidgetChanged().emit(container->widget(0));
        hasEmittedChanged_ = true;
      }
    } else {
      currentWidgetChanged().emit(currentWidget());
      hasEmittedChanged_ = true;
    }
  }
}

In WStackedWidget it's now more restrictive if (container->count() > 0), maybe Lazy or deferred widgets don't get a signal due to this?
In WMenu the code-section that emits the currentWidgetChanged signal was completely removed.

Actions #4

Updated by Matthias Van Ceulebroeck 4 days ago

  • Status changed from New to InProgress
  • Assignee set to Matthias Van Ceulebroeck
  • Target version set to 4.12.2
Actions #5

Updated by Matthias Van Ceulebroeck about 4 hours ago

  • Status changed from InProgress to Review
  • Assignee changed from Matthias Van Ceulebroeck to Romain Mardulyn
Actions #6

Updated by Matthias Van Ceulebroeck about 4 hours ago

  • Status changed from Review to Implemented @Emweb
  • Assignee changed from Romain Mardulyn to Matthias Van Ceulebroeck
  • % Done changed from 0 to 100
Actions

Also available in: Atom PDF