Project

General

Profile

WNavigationBar::removeWidget unexpected behaviour

Added by Christian Meyer about 2 years ago

Hi, it's me again =D

I am currently building a mixed Application, with parts that are publicly accessible and some other part only for registered Users

There is a WNavigationBar at the top with some items, and inside the useraccess item, the login form is placed
within a WStackedWidget.

On Login, the Auth::AuthWidget is removed fine from the container and placed into the WNavigationBar
On Logout, I would like to move it back into the container, but removeWidget always returns nullptr

here is some code:

getAuthSession()->login().changed().connect([this](){
    if(this->getAuthSession()->login().loggedIn())
    {
        // Login: Move Logout to Navbar
        auto authPtr = this->sCon->removeWidget(this->authWidget);
        this->authWidget = authPtr.get();
        this->navbar->addWidget(std::move(authPtr),Wt::AlignmentFlag::Right);

        this->sCon->clear();
        this->sCon->addNew<Wt::WText>("Login Success");
    }
    else
    {
        // Logout: clear all, then Add Login Widget
        auto authPtr = this->navbar->removeWidget(this->authWidget); // returns nullptr
        this->sCon->clear();
        this->authWidget = 
            this->sCon->addWidget(std::move(authPtr)); // understandable Segfault, moving nullptr
    }
});

What I found was that WNavigationBar is inheriting from WTemplate and there the usual function seems to be overridden to return a bound widget.

After looking through the code of WNavigationBar it seems like something like this should work:

std::unique_ptr<WWidget>
WNavigationBar::removeWidget(WWidget *widget)
{
  auto remWidget = WTemplate::removeWidget(widget);
  if(remWidget)
    return std::move(remWidget);

  WContainerWidget *contents = resolve<WContainerWidget *>("contents");
  return contents->removeWidget(widget);
}

Is my assumption correct?

If so, please feel free to add this into the Library.

Best Regards
Christian


Replies (1)

RE: WNavigationBar::removeWidget unexpected behaviour - Added by Christian Meyer about 2 years ago

It seems I was almost correct...
I missed the wrap inside NavBar

this does work now in my code:

getAuthSession()->login().changed().connect([this](){
    // keep AuthWidget if Problem with Registration (edgecase)
    if(this->getAuthSession()->login().state() == Wt::Auth::LoginState::Disabled)
        return;

    if(this->getAuthSession()->login().loggedIn())
    {
        // Login: Move Logout to Navbar
        auto authPtr = this->sCon->removeWidget(this->authWidget);
        this->authWidget = authPtr.get();
        this->navbar->addWidget(std::move(authPtr),Wt::AlignmentFlag::Right);

        this->sCon->clear();
        this->sCon->addNew<Wt::WText>("Login Success"); // Or Setup Access content
    }
    else
    {
        // Logout: clear all, then Add Login Widget
        auto contents = this->navbar->resolve<Wt::WContainerWidget *>("contents");
        std::unique_ptr<Wt::Auth::AuthWidget> authPtr;
        for(auto wrap : contents->children())
        {
            authPtr = wrap->removeWidget(this->authWidget);
            if(authPtr)
                break;
        }

        this->sCon->clear(); // remove access content
        if(authPtr)
            this->sCon->addWidget(std::move(authPtr));
    }
});

So then this should be dropin ready for Wt:

std::unique_ptr<WWidget>
WNavigationBar::removeWidget(WWidget *widget)
{
  auto remWidget = WTemplate::removeWidget(widget);
  if(remWidget)
    return std::move(remWidget);

  WContainerWidget *contents = resolve<WContainerWidget *>("contents");
  for(auto wrap : contents->children())
  {
    remWidget = wrap->removeWidget(this->authWidget);
    if(remWidget)
      break;
  }
  return std::move(remWidget);
}

Cheers
Christian

    (1-1/1)