Project

General

Profile

WT3 to WT4 Port

Added by Sebastian Fett almost 5 years ago

Since apparently a lot has changed between 3 and 4 and I just started with c a few months ago, I am not to familiar with the whole smart pointer thing.

How would I for example rewrite this Code:

void UserScreen::populateSubMenu(Wt::WMenu *menu)

{

menu~~addItem("Home", home())~~>setPathComponent("");

menu->addItem("Create New Model",

deferCreate(boost::bind

(&UserScreen::createNewModel, this)));

menu->addItem("List of Models",

deferCreate(boost::bind

(&UserScreen::listOfModels, this)));

menu->addItem("Simulations",

deferCreate(boost::bind

(&UserScreen::listOfSimulations, this)));

menu->addItem("Queue of Job(s)",

deferCreate(boost::bind

(&UserScreen::queueOfJobs, this)));

}

>

the submethods create new containerwidgets which should be accessible from the menu.

If I've understood it correctly im expected to create a new unique menuitem which i then add to the menu itself. I struggle to find the point where i link the new menuitem to the widget which is created from a template in the subroutine (say createnewmodel)

Wt::WWidget *UserScreen::createNewModel()

{

Wt::WTemplate *result = new TopicTemplate("fpm-CreateNewModel");

Wt::WPushButton *button_filling = new Wt::WPushButton("Filling");

button_filling->setLink(Wt::WLink(Wt::WLink::InternalPath, "/createmodel/filling"));

Wt::WPushButton *button_sloshing = new Wt::WPushButton("Sloshing");

button_sloshing->setLink(Wt::WLink(Wt::WLink::InternalPath, "/createmodel/sloshing"));

Wt::WPushButton *button_custom = new Wt::WPushButton("Custom Model");

button_custom->setLink(Wt::WLink(Wt::WLink::InternalPath, "/createmodel/custommodel"));

result->bindWidget("FillingPushButtonLink", button_filling);

result->bindWidget("SloshingPushButtonLink", button_sloshing);

result->bindWidget("CustomPushButtonLink", button_custom);

return result;

}

>


Replies (19)

RE: WT3 to WT4 Port - Added by lm at almost 5 years ago

In general, a std::unique_ptr is very much like a normal pointer except two things: 1) you can only have one of them, and 2) it will delete itself when you're done with it.

To explain just a little more regarding point 1, consider the following:

Wt::WWidget * thing = new Wt::WWidget;
Wt::WWidget * other_thing = thing;

In that code, you copy "thing" to "other_thing". Using std::unique_ptr, this is simply not possible. It's unique. You can only have one of them. If you want to pass the std::unique_ptr somewhere, you have to move it:

std::unique_ptr<Wt::WWidget> thing = std::make_unique<Wt::WWidget>(); /* Calls ::new for you and passes no arguments to the constructor. */
std::unique_ptr<Wt::WWidget> other_thing = thing; /* COMPILATION ERROR */
std::unique_ptr<Wt::WWidget> other_thing = std::move(thing);

After moving the pointer from "thing", "thing" now only points to null and won't be handling the widget you created. Feel free to speak up if you have any more questions about this.

Regarding your first code example, what compilation errors are you getting? I don't know that I've created a Wt::WMenu before, so if this compiles, it looks good to me!

I'm guessing the second example doesn't compile at result->bindWidget(..., button_filling); etc. (reference at https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WTemplate.html). Instead, you'll need to make std::unique_ptrs for each of those buttons and pass that in. Perhaps

result->bindWidget("FillingPushButtonLink", std::unique_ptr(button_filling));

By the way, use < pre> and < /pre> (without spaces) to format code excerpts. Also, you can use "Preview" at the bottom to make sure it's all right before submitting..

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

    void UserScreen::populateSubMenu(Wt::WMenu *menu) {
    menu->addItem("Create New Model",
    deferCreate(boost::bind
    (&UserScreen::createNewModel, this)));
    }

errors with:

UserScreen.C: In member function 'void UserScreen::populateSubMenu(Wt::WMenu*)':

UserScreen.C:557:45: error: no matching function for call to 'Wt::WMenu::addItem(Wt::WString, DeferredWidget<std::_Bind<Wt::WWidget* (UserScreen::)()> >)'

557 | (&UserScreen::listOfModels, this)));

| ^

In file included from UserScreen.h:13,

from UserScreen.C:1:

/p/tv/local/redhat/wt-4.0.5-libs/include/Wt/WMenu.h:138:14: note: candidate: 'Wt::WMenuItem* Wt::WMenu::addItem(const Wt::WString&, std::unique_ptrWt::WWidget, Wt::ContentLoading)'

138 | WMenuItem *addItem(const WString& label,

| ^

/p/tv/local/redhat/wt-4.0.5-libs/include/Wt/WMenu.h:139:33: note: no known conversion for argument 2 from 'DeferredWidget<std::_Bind<Wt::WWidget* (UserScreen::)()> >' to 'std::unique_ptrWt::WWidget'

139 | std::unique_ptr contents = nullptr,

|

/p/tv/local/redhat/wt-4.0.5-libs/include/Wt/WMenu.h:158:14: note: candidate: 'Wt::WMenuItem* Wt::WMenu::addItem(const string&, const Wt::WString&, std::unique_ptrWt::WWidget, Wt::ContentLoading)'

158 | WMenuItem *addItem(const std::string& iconPath, const WString& label,

| ^

/p/tv/local/redhat/wt-4.0.5-libs/include/Wt/WMenu.h:158:41: note: no known conversion for argument 1 from 'Wt::WString' to 'const string&' {aka 'const std::basic_string&'}

158 | WMenuItem *addItem(const std::string& iconPath, const WString& label,

|

/p/tv/local/redhat/wt-4.0.5-libs/include/Wt/WMenu.h:172:16: note: candidate: 'template<class T, class V> Wt::WMenuItem* Wt::WMenu::addItem(const Wt::WString&, T*, void (V::)())'

172 | WMenuItem *addItem(const WString& text, T *target, void (V::*method)());

| ^

/p/tv/local/redhat/wt-4.0.5-libs/include/Wt/WMenu.h:172:16: note: template argument deduction/substitution failed:

UserScreen.C:557:45: note: candidate expects 3 arguments, 2 provided

557 | (&UserScreen::listOfModels, this)));

| ^

In file included from UserScreen.h:13,

from UserScreen.C:1:

/p/tv/local/redhat/wt-4.0.5-libs/include/Wt/WMenu.h:185:16: note: candidate: 'template<class T, class V> Wt::WMenuItem* Wt::WMenu::addItem(const string&, const Wt::WString&, T
, void (V::*)())'

185 | WMenuItem *addItem(const std::string& iconPath, const WString& text,

| ^

/p/tv/local/redhat/wt-4.0.5-libs/include/Wt/WMenu.h:185:16: note: template argument deduction/substitution failed:

UserScreen.C:557:45: note: candidate expects 4 arguments, 2 provided

557 | (&UserScreen::listOfModels, this)));

>

Which is likely because createNewModel returns a Wt::WWidget pointer instead of a std::unique_ptrWt::WWidget due to

 Wt::WMenuItem* Wt::WMenu::addItem(const Wt::WString&, std::unique_ptr<Wt::WWidget>, Wt::ContentLoading) 

needing a menu (unique pointer) to add the widget(unique pointer) with a label(WString) and give me a WMenuItem pointer. This requires me to pretty much change at least 75% of the code. Is that a correct assumption?

RE: WT3 to WT4 Port - Added by lm at almost 5 years ago

No, you need to rewrite your deferCreate function. Currently, it returns a DeferredWidget<...>*, but it should return a std::unique_ptr<DeferredWidget<...>>. See https://www.webtoolkit.eu/wt/doc/examples/html/Home_8h.html . I guess you lifted deferCreate from the examples at some point way back, but since then it has changed.

I've never seen this DeferredWidget, but you're probably right that UserScreen::createNewModel will need to return a std::unique_ptr.

"This requires me to pretty much change at least 75% of the code. Is that a correct assumption?"

I'm not sure, but that might be a ballpark estimate depending on how much of your code is widget creation. Logic code shouldn't have to change too much. If you use that deferCreate function a lot, maybe just changing it will help? Or, you know, you could rewrite deferCreate (or DeferredWidget) so that it takes a function that generates a Wt::WWidget * and converts it to a std::unique_ptr? If you're really fancy, the DeferredWidget can detect whether the function will return a std::unique_ptr or a normal pointer and convert only if necessary ;-)

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

The initial code was not written by me and I would have used JS or python since I'm comfortable with those.

Changing one method is not the big issue but it seems like you have to change every single object creation, every button, every submenu, every linking to buttons, every lambda expression etc. Thats quite depressing for me ....

Anyway, thanks for the help, I might post here if I get stuck again.

RE: WT3 to WT4 Port - Added by lm at almost 5 years ago

Sorry to bear the bad news, but yes, it's a fairly invasive change. I trust you'll grow more comfortable with what's going on, and perhaps this will be your chance to get quite familiar with the code base (even if you didn't want to be this familiar with the code base!). Good luck!

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

Okay it seems like I am almost done with the complete port, everything at least compiles. Getting a few runtime errors cuz moving a unique pointer twice isnt a good idea or moving it and adding to it later so I had to change a few orders and stuff.

But now I ran into an error which I have no idea what it could be caused by

 [2019-Jun-17 11:16:28.031] 9536 [/ AQfjZaWsvnIyQGu6] [error] "WApplication: JavaScript error: {""exception_description"":""a is null"",""exception_js"":""Wt._p_.response(-277101984);Wt._p_.setSessionUrl('?wtd=AQfjZaWsvnIyQGu6');Wt4_0_5.FlexLayout = function(i,h){function j(){var b=f.getElement(h);if(b){b=b.childNodes;for(var g=0;g<b.length;++g){var e=b[g];if(!(e.style.display==\""none\""||$(e).hasClass(\""out\"")||e.className==\""resize-sensor\"")){var c=f.css(e,\""overflow\"");if(c===\""visible\""||c===\""\"")e.style.overflow=\""hidden\""}}}}var f=i.WT;setTimeout(j,0);this.adjust=function(){setTimeout(function(){var b=f.getElement(h);if(b){b=b.childNodes;for(var g=0,e=f.styleAttribute(\""flex-grow\""),c=0;c<b.length;++c){var a= b[c];if(!(a.style.display==\""none\""||$(a).hasClass(\""out\"")||a.className==\""resize-sensor\"")){var d=a.getAttribute(\""flg\"");if(d!==\""0\""){d=f.css(a,e);g+=parseFloat(d)}}}for(c=0;c<b.length;++c){a=b[c];if(!(a.style.display==\""none\""||$(a).hasClass(\""out\"")||a.className==\""resize-sensor\"")){a.resizeSensor&&a.resizeSensor.trigger();if(g===0)d=1;else{d=a.getAttribute(\""flg\"");if(d===\""0\"")d=0;else d=d=f.css(a,e)}a.style[e]=d}}}},0)}}\nWt4_0_5.WDialog = function(h,a,i,D,m,n,w,x,E){function y(){if(w){var b=c.pxself(a,\""left\""),d=c.pxself(a,\""top\"");if(b!=s||d!=t){s=b;t=d;h.emit(a,w,s,t)}}}function z(b,d){if(!A)if(b!=u||d!=v){u=b;v=d;x&&h.emit(a,x,u,v)}}function F(b){var d=b||window.event;b=c.pageCoordinates(d);d=c.windowCoordinates(d);var e=c.windowSize();if(d.x>0&&d.x<e.x&&d.y>0&&d.y<e.y){m=n=false;if(a.style.right===\""auto\""||a.style.right===\""\""){a.style.left=c.px(a,\""left\"")+b.x-o+\""px\"";a.style.right= \""\""}else{a.style.right=c.px(a,\""right\"")+o-b.x+\""px\"";a.style.left=\""auto\""}if(a.style.bottom===\""auto\""||a.style.bottom===\""\""){a.style.top=c.px(a,\""top\"")+b.y-p+\""px\"";a.style.bottom=\""\""}else{a.style.bottom=c.px(a,\""bottom\"")+p-b.y+\""px\"";a.style.top=\""auto\""}o=b.x;p=b.y}}function G(b,d,e,f){if(a.style.position==\""\"")a.style.position=c.isIE6?\""absolute\"":\""fixed\"";a.style.visibility=\""visible\"";c.windowSize();k=c.parsePct(a.style.height,k);l=c.parsePct(a.style.width,l);if(f){a.style.height=Math.max(0,e)+\""px\"";a.style.width=Math.max(0, d)+\""px\""}z(d,e);j.centerDialog();b=l!=-1;f=k!=-1;if(b&&f){q=B();r=C();j.onresize(q,r,true)}else if(b){q=B();j.onresize(q,e,true)}else if(f){r=C();j.onresize(d,r,true)}}function B(){return c.windowSize().x*l/100}function C(){return c.windowSize().y*k/100}function H(b,d,e,f){if(f){if(d>0)g.style.width=d+c.parsePx($(g).css(\""borderLeftWidth\""))+c.parsePx($(g).css(\""borderRightWidth\""))+\""px\"";if(e>0)g.style.height=e+c.parsePx($(g).css(\""borderTopWidth\""))+c.parsePx($(g).css(\""borderBottomWidth\""))+\""px\""}j.centerDialog(); a.wtResize&&a.wtResize(a,d,e,true)}function I(){j.centerDialog();h.layouts2&&h.layouts2.adjust()}jQuery.data(a,\""obj\"",this);var j=this,g=$(a).find(\"".dialog-layout\"").get(0),c=h.WT,o,p,s=-1,t=-1,u=-1,v=-1,A=false,l=-1,k=-1,q=-1,r=-1;if(i&&D){i.onmousedown=function(b){b=b||window.event;c.capture(i);b=c.pageCoordinates(b);o=b.x;p=b.y;i.onmousemove=F};i.onmouseup=function(){i.onmousemove=null;y();c.capture(null)}}this.centerDialog=function(){var b=c.parsePct(c.css(a,\""max-width\""),0),d=c.parsePct(c.css(a, \""max-height\""),0);if(b!==0){var e=c.windowSize(),f=jQuery.data(g.firstChild,\""layout\"");f&&f.setMaxSize&&f.setMaxSize(e.x*b/100,e.y*d/100)}if(a.parentNode==null)a=i=null;else if(a.style.display!=\""none\""){e=c.windowSize();b=a.offsetWidth;d=a.offsetHeight;if(l!=-1)m=true;if(k!=-1)n=true;if(m){a.style.left=Math.round((e.x-b)/2+(c.isIE6?document.documentElement.scrollLeft:0))+\""px\"";a.style.marginLeft=\""0px\""}if(n){a.style.top=Math.round((e.y-d)/2+(c.isIE6?document.documentElement.scrollTop:0))+\""px\"";a.style.marginTop= \""0px\""}if(a.style.position!=\""\"")a.style.visibility=\""visible\"";y()}};this.bringToFront=function(){var b=0;$(\"".Wt-dialog, .modal, .modal-dialog\"").each(function(e,f){b=Math.max(b,$(f).css(\""z-index\""))});if(b>a.style.zIndex){var d=b+1;a.style.zIndex=d;h.emit(a,E,d)}};this.onresize=function(b,d,e){m=n=false;A=!e;H(a,b,d,true);var f=jQuery.data(g.firstChild,\""layout\"");f&&f.setMaxSize&&f.setMaxSize(0,0);h.layouts2&&h.layouts2.scheduleAdjust();e&&z(b,d)};g.wtResize=G;a.wtPosition=I;if(a.style.width!=\""\"")g.style.width= c.parsePx(a.style.width)>0?a.style.width:a.offsetWidth+\""px\"";if(a.style.height!=\""\"")g.style.height=c.parsePx(a.style.height)>0?a.style.height:a.offsetHeight+\""px\"";j.centerDialog()}\nWt4_0_5.ChildrenResize = function() { return (function(a,f,d,e){function h(i){var j=b.px(i,\""marginTop\"");j+=b.px(i,\""marginBottom\"");if(!b.boxSizing(i)){j+=b.px(i,\""borderTopWidth\"");j+=b.px(i,\""borderBottomWidth\"");j+=b.px(i,\""paddingTop\"");j+=b.px(i,\""paddingBottom\"")}return j}var b=this,l=d>=0;if(e)if(l){a.style.height=d+\""px\"";a.lh=true}else{a.style.height=\""\"";a.lh=false}else a.lh=false;if(b.boxSizing(a)){d-=b.px(a,\""marginTop\"");d-=b.px(a,\""marginBottom\"");d-=b.px(a,\""borderTopWidth\"");d-=b.px(a,\""borderBottomWidth\""); d-=b.px(a,\""paddingTop\"");d-=b.px(a,\""paddingBottom\"");f-=b.px(a,\""marginLeft\"");f-=b.px(a,\""marginRight\"");f-=b.px(a,\""borderLeftWidth\"");f-=b.px(a,\""borderRightWidth\"");f-=b.px(a,\""paddingLeft\"");f-=b.px(a,\""paddingRight\"")}var g,c;e=0;for(g=a.childNodes.length;e<g;++e){c=a.childNodes[e];if(c.nodeType==1&&!$(c).hasClass(\""wt-reparented\""))if(l){var k=d-h(c);if(k>0){if(c.offsetTop>0){var m=b.css(c,\""overflow\"");if(m===\""visible\""||m===\""\"")c.style.overflow=\""auto\""}if(c.wtResize)c.wtResize(c,f,k,true);else{k=k+\""px\"";if(c.style.height!= k){c.style.height=k;c.lh=true}}}}else if(c.wtResize)c.wtResize(c,f,-1,true);else{c.style.height=\""\"";c.lh=false}}}).apply(Wt4_0_5, arguments) };{Wt._p_.setSessionUrl('?wtd=AQfjZaWsvnIyQGu6');Wt4_0_5.WPopupWidget = function(h,b,s,t,u){function o(){if(e.isIOS){$(document).bind(\""touchstart\"",p);$(document).bind(\""touchend\"",q)}else $(document).bind(\""click\"",i)}function v(){if(e.isIOS){$(document).unbind(\""touchstart\"",p);$(document).unbind(\""touchend\"",q)}else $(document).unbind(\""click\"",i)}function p(a){a=a.originalEvent.touches;f=a.length>1?null:{x:a[0].screenX,y:a[0].screenY}}function q(a){if(f){var c=a.originalEvent.changedTouches[0];Math.abs(f.x-c.screenX)< 20&&Math.abs(f.y-c.screenY)<20&&i(a)}}function w(){clearTimeout(j);if(k>0)j=setTimeout(function(){l.hide()},k)}function x(){clearTimeout(j)}function y(){return b.style.display==\""hidden\""}function i(a){function c(r,d){if(r==d)return true;for(d=d.parentNode;d;d=d.parentNode)if(r==d)return true;return false}a=e.target(a);if(a==document)if(e.WPopupWidget.popupClicked!==null)a=e.WPopupWidget.popupClicked;c(b,a)||l.hide()}jQuery.data(b,\""popup\"",this);var l=this,e=h.WT,j=null,g=s,k=t,f=null,m=null,n=null; this.bindShow=function(a){m=a};this.bindHide=function(a){n=a};this.shown=function(){g&&setTimeout(function(){o()},0);m&&m()};this.show=function(a,c){if(b.style.display!=\""\""){b.style.display=\""\"";a&&e.positionAtWidget(b.id,a.id,c);h.emit(b,\""shown\"")}};this.hidden=function(){n&&n();g&&v()};this.hide=function(){if(b.style.display!=\""none\"")b.style.display=\""none\"";h.emit(b,\""hidden\"");l.hidden()};this.setTransient=function(a,c){g=a;k=c;g&&!y()&&setTimeout(function(){o()},0)};$(b).mouseleave(w).mouseenter(x);u&& this.shown()}\nWt4_0_5.addCss('div.Wt-dialog','left: 0px; top: 0px;');\nWt4_0_5.addCss('.Wt-notselected .Wt-popupmenu','visibility: hidden;');\nvar j2=Wt4_0_5.$('oy65sp5');\nWt4_0_5.setHtml(j2, '');\nfunction f3(event) { var e=event||window.event,o=this;if(e.keyCode && (e.keyCode == 27)){Wt._p_.update(o,'s44',e,true);}}\nWt._p_.bindGlobal('keydown', 'oy65sp7', f3)\nvar j4=Wt4_0_5.$('oy65sp7');\nvar j5=document.createElement('span');j4.appendChild(j5);\nj5.setAttribute('id', 'oy65so0');\n\nj5.style.display='none';\nWt4_0_5.setHtml(j5,'...');\nvar j6=document.createElement('div');j4.appendChild(j6);\nj6.setAttribute('id', 'oy65smh');\n\nj6.className='Wt-popup modal-dialog Wt-dialog';\nj6.style.position='fixed';\nj6.style.zIndex='200';\nj6.style.marginTop='0.0px';\nj6.style.marginRight='0.0px';\nj6.style.marginBottom='0.0px';\nj6.style.marginLeft='0.0px';\nfunction f7(event) { var e=event||window.event,o=this;if($(o).hasClass('disabled')){Wt4_0_5.cancelEvent(e);return;}Wt._p_.update(o,'s61',e,true);}\nj6.onmousedown=f7;\nfunction f8(event) { var e=event||window.event,o=this;if($(o).hasClass('disabled')){Wt4_0_5.cancelEvent(e);return;}(function(o,e) {  if (Wt4_0_5.WPopupWidget && $.data(o,'popup')) {Wt4_0_5.WPopupWidget.popupClicked = o;$(document).trigger('click', e);Wt4_0_5.WPopupWidget.popupClicked = null; }})(o,e);Wt4_0_5.cancelEvent(e,0x1);}\nj6.onclick=f8;\nfunction f9(event) { var e=event||window.event,o=this;if(e.keyCode && (e.keyCode == 13)){Wt._p_.update(o,'s43',e,true);}if(e.keyCode && (e.keyCode == 27)){Wt._p_.update(o,'s45',e,true);}}\nj6.onkeydown=f9;\nWt4_0_5.setHtml(j6,'\\r\\n    \\r\\n    <div id=\""oy65smf\"" class=\""modal-content dialog-layout movable\"" style=\""max-width:999999.0px;display:flex;flex-flow:column;-moz-flex-flow:column;\""><div id=\""oy65smc\"" flg=\""0\"" class=\""modal-header\"" style=\""flex:0 0 auto;-moz-flex:0 0 auto;\""><div id=\""oy65smb\""><h4>OS-Configuration</h4></div></div><div id=\""oy65sma\"" class=\""modal-body Wt-msgbox-body\"" style=\""flex:1 1 auto;-moz-flex:1 1 auto;\""><i id=\""oy65sm7\"" class=\""fa fa-info Wt-msgbox-icon\"" style=\""font-size:2.5em;\""></i><div id=\""oy65sm6\"" class=\""Wt-msgbox-text\""><p>The currently running OS is Unix.</p></div></div><div id=\""oy65sm4\"" flg=\""0\"" class=\""modal-footer\"" style=\""flex:0 0 auto;-moz-flex:0 0 auto;\""><button id=\""oy65sm2\"" type=\""button\"" onclick=\""var e=event||window.event,o=this;if($(o).hasClass(\\'disabled\\')){Wt4_0_5.cancelEvent(e);return;}Wt._p_.update(o,\\'s41\\',e,true);\"" class=\""btn btn-primary with-label\"">Ok</button></div></div>\\r\\n  ');\nnew Wt4_0_5.WDialog(Wt,Wt4_0_5.$('oy65smh'),Wt4_0_5.$('oy65smc'),1,1,1,null,null,\""zIndexChanged\"");\nnew Wt4_0_5.WPopupWidget(Wt,Wt4_0_5.$('oy65smh'),false,0,true);\n\nvar j10=document.createElement('div');j4.appendChild(j10);\nj10.setAttribute('id', 'oy65sm1');\nj10.setAttribute('data-object-name','dialog-cover');\nj10.style.display='none';\nj2.className='';\nj2.style.display='flex';\nj2.style.boxSizing='border-box';\nj2.style.flexFlow='row';\nWt4_0_5.setHtml(j2,'<div id=\""oy65so1\"" flg=\""0\"" style=\""margin:0px 3px 0px 3px;overflow-x:hidden;overflow-y:hidden;flex:1 1 auto;-moz-flex:1 1 auto;\""></div>');\nWt4_0_5.$('oy65sp5').layout=new Wt4_0_5.FlexLayout(Wt,'oy65sp5');;\n\nvar j11=Wt4_0_5.$('oy65sod');\nWt4_0_5.setHtml(j11,'\\r\\n    <div class=\""Wt-auth-logged-in\"">\\r\\n      <b>test123</b> <button id=\""oy65sjm\"" data-object-name=\""logout\"" type=\""button\"" onclick=\""var e=event||window.event,o=this;if($(o).hasClass(\\'disabled\\')){Wt4_0_5.cancelEvent(e);return;}Wt._p_.update(o,\\'s5f\\',e,true);\"" class=\""btn btn-default with-label\"">Logout</button>\\r\\n    </div>\\r\\n  ',false);\nWt4_0_5.$('oy65so1').wtEncodeValue = function() {return Wt4_0_5.$('oy65so1').scrollTop + ';' + Wt4_0_5.$('oy65so1').scrollLeft;}\nWt._p_.setTitle('FPMWeb');\nfunction f1(event) { var e=event||window.event,o=this;if(e.keyCode && (e.keyCode == 13)){Wt._p_.update(o,'s42',e,true);}}\nWt._p_.bindGlobal('keydown', 'oy65smf', f1)\nWt4_0_5.$('oy65smf').layout=new Wt4_0_5.FlexLayout(Wt,'oy65smf');;\n\n\nWt._p_.setFormObjects(['oy65so1']);}document.body.parentNode.className=' Wt-layout';document.body.className=' Wt-layout Wt-ltr';document.body.setAttribute('dir', 'LTR');{var j12=Wt4_0_5.$('oy65so0');\nvar j13=document.createElement('div');j12.parentNode.replaceChild(j13,j12);\nj13.setAttribute('id', 'oy65so0');\n\nj13.className='Wt-popup modal-dialog Wt-dialog';\nj13.style.position='fixed';\nj13.style.zIndex='100';\nj13.style.display='none';\nfunction f14(event) { var e=event||window.event,o=this;if($(o).hasClass('disabled')){Wt4_0_5.cancelEvent(e);return;}(function(o,e) {  if (Wt4_0_5.WPopupWidget && $.data(o,'popup')) {Wt4_0_5.WPopupWidget.popupClicked = o;$(document).trigger('click', e);Wt4_0_5.WPopupWidget.popupClicked = null; }})(o,e);Wt4_0_5.cancelEvent(e,0x1);}\nj13.onclick=f14;\nWt4_0_5.setHtml(j13,'\\r\\n    \\r\\n    <div id=\""oy65snx\"" class=\""modal-content dialog-layout movable\"" style=\""max-width:999999.0px;display:flex;flex-flow:column;-moz-flex-flow:column;\""><div id=\""oy65snu\"" flg=\""0\"" class=\""modal-header\"" style=\""flex:0 0 auto;-moz-flex:0 0 auto;\""><div id=\""oy65snt\""><h4>About Information</h4></div></div><div id=\""oy65sns\"" class=\""modal-body Wt-msgbox-body\"" style=\""flex:1 1 auto;-moz-flex:1 1 auto;\""><i id=\""oy65snp\"" class=\""fa fa-info Wt-msgbox-icon\"" style=\""font-size:2.5em;\""></i><span id=\""oy65sno\"" class=\""Wt-msgbox-text\""><p> UserID is  </p></span></div><div id=\""oy65snm\"" flg=\""0\"" class=\""modal-footer\"" style=\""flex:0 0 auto;-moz-flex:0 0 auto;\""><button id=\""oy65snk\"" type=\""button\"" onclick=\""var e=event||window.event,o=this;if($(o).hasClass(\\'disabled\\')){Wt4_0_5.cancelEvent(e);return;}Wt._p_.update(o,\\'s22\\',e,true);\"" class=\""btn btn-default with-label\"">Ok</button></div></div>\\r\\n  ');\nnew Wt4_0_5.WDialog(Wt,Wt4_0_5.$('oy65so0'),Wt4_0_5.$('oy65snu'),1,1,1,null,null,\""zIndexChanged\"");\nnew Wt4_0_5.WPopupWidget(Wt,Wt4_0_5.$('oy65so0'),false,0,false);\nnew Wt4_0_5.WDialog(Wt,Wt4_0_5.$('oy65so0'),Wt4_0_5.$('oy65snu'),1,1,1,null,null,\""zIndexChanged\"");\n\nWt4_0_5.unstub(j12,j13,1);\nWt4_0_5.$('oy65snx').layout=new Wt4_0_5.FlexLayout(Wt,'oy65snx');;\n\n}Wt4_0_5.addStyleSheet('/resources/font-awesome/css/font-awesome.min.css', 'all');\n Wt4_0_5.addStyleSheet('/style/everywidget.css', 'all');\n Wt4_0_5.addStyleSheet('/style/dragdrop.css', 'all');\n "",""stack"":""window.Wt4_0_5</this.setHtml@http://0.0.0.0:8080/:32:176\n@http://0.0.0.0:8080/ line 96 > eval:48:1\n@http://0.0.0.0:8080/ line 96 > eval:1:2\nya`http://0.0.0.0:8080/:96:480\nua@http://0.0.0.0:8080/:97:261\ny@http://0.0.0.0:8080/:28:198\nz`http://0.0.0.0:8080/:28:392\n""}"

Code is basically the same as in the wt3 example which runs smoothly.

For me the JS part seems like the top element is not defined and therefore gives me a null, can that be caused by moving the layouts unique pointer after changing the path?

Loging in works fine, then this appears.

In case any1 has some spare time, could you tell me why the same code for the login screen delivers different results in wt3 and wt4 (screenshots are added)

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

[2019-Jun-18 11:45:49.812] 12522 [/ nWC7MuaoPZGtvxq8] [error] "WContainerWidget: removeWidget(): widget not in container"

this occurs right before the error, server is still running, just a JS object called a is null.

No idea what could cause this.

The error message "Internal WT error, a is a null" is not really helpful either

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

Afters tons and tons of gdbing I figured out that the error is caused by this line

     auto layout = root()->setLayout(std::make_unique<Wt::WHBoxLayout>()); 

in the main method. Commenting out everything afterwards still gave me the

 [/ uWbvmKOUdrof1SL0] [error] "WApplication: JavaScript error: {""exception_description"":""a is null"",""exception_js"":""Wt._p_.response(-935711506);Wt._p_.setSessionUrl('?wtd=uWbvmKOUdrof1SL0');Wt4_0_5.FlexLayout = function(i,h){function j(){var b=f.getElement(h);if(b){b=b.childNodes;for(var g=0;g<b.length;++g){var e=b[g];if(!(e.style.display==\""none\""||$(e).hasClass(\""out\"")||e.className==\""resize-sensor\"")){var c=f.css(e,\""overflow\"");if(c===\""visible\""||c===\""\"")e.style.overflow=\""hidden\""}}}}var f=i.WT;setTimeout(j,0);this.adjust=function(){setTimeout(function(){var b=f.getElement(h);if(b){b=b.childNodes;for(var g=0,e=f.styleAttribute(\""flex-grow\""),c=0;c<b.length;++c){var a= b[c];if(!(a.style.display==\""none\""||$(a).hasClass(\""out\"")||a.className==\""resize-sensor\"")){var d=a.getAttribute(\""flg\"");if(d!==\""0\""){d=f.css(a,e);g+=parseFloat(d)}}}for(c=0;c<b.length;++c){a=b[c];if(!(a.style.display==\""none\""||$(a).hasClass(\""out\"")||a.className==\""resize-sensor\"")){a.resizeSensor&&a.resizeSensor.trigger();if(g===0)d=1;else{d=a.getAttribute(\""flg\"");if(d===\""0\"")d=0;else d=d=f.css(a,e)}a.style[e]=d}}}},0)}}\n{Wt._p_.setSessionUrl('?wtd=uWbvmKOUdrof1SL0');var j1=Wt4_0_5.$('onygajm');\nWt4_0_5.setHtml(j1, '');\nj1.className='';\nj1.style.padding='9px 6px 9px 6px';\nj1.style.display='flex';\nj1.style.boxSizing='border-box';\nj1.style.flexFlow='row';\nWt4_0_5.$('onygajm').layout=new Wt4_0_5.FlexLayout(Wt,'onygajm');;\n\nvar j2=Wt4_0_5.$('onygaiu');\nWt4_0_5.setHtml(j2,'\\r\\n    <div class=\""Wt-auth-logged-in\"">\\r\\n      <b>test123</b> <button id=\""onygaii\"" data-object-name=\""logout\"" type=\""button\"" onclick=\""var e=event||window.event,o=this;if($(o).hasClass(\\'disabled\\')){Wt4_0_5.cancelEvent(e);return;}Wt._p_.update(o,\\'s14\\',e,true);\"" class=\""btn with-label\"">Logout</button>\\r\\n    </div>\\r\\n  ',false);\n\nWt._p_.setFormObjects([]);}document.body.parentNode.className=' Wt-layout';document.body.className=' Wt-layout Wt-ltr';document.body.setAttribute('dir', 'LTR');"",""stack"":""window.Wt4_0_5</this.setHtml@http://0.0.0.0:8080/:32:176\n@http://0.0.0.0:8080/ line 96 > eval:12:1\n@http://0.0.0.0:8080/ line 96 > eval:1:2\nya`http://0.0.0.0:8080/:96:480\nua@http://0.0.0.0:8080/:97:261\ny@http://0.0.0.0:8080/:28:198\nz`http://0.0.0.0:8080/:28:392\n""}"

Error.

MAIN:::::

 #include <Wt/WString.h>           // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WString.html
// - - Basic Widgets
#include <Wt/WApplication.h>        // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WApplication.html
#include <Wt/WEnvironment.h>        // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WEnvironment.html
#include <Wt/WBootstrapTheme.h>     // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WBootstrapTheme.html
#include <Wt/WCssTheme.h>           // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WCssTheme.html
#include <Wt/WContainerWidget.h>        // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WContainerWidget.html
#include <Wt/WHBoxLayout.h>     // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WHBoxLayout.html
#include <Wt/WImage.h>          // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WImage.html
// - - Web Application Server
#include <Wt/WServer.h>         // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WServer.html
// - - Database
#include <Wt/Dbo/Dbo.h>         // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Dbo_1_1Dbo.html
// - - Authentication
#include <Wt/Auth/AuthModel.h>      // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Auth_1_1AuthModel.html
#include <Wt/Auth/AuthWidget.h>     // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Auth_1_1AuthWidget.html
#include <Wt/Auth/PasswordService.h>    // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Auth_1_1PasswordService.html
// - Custom Inclusions
#include "main.h"           // Prototype definition of class "AuthApplication" and its functions.
#include "model/Session.h"      // 
#include "BaseScreen.h"         // 



// +--------------------------------+
// | Class-Function-Implementations |
// +--------------------------------+

AuthApplication::AuthApplication(const Wt::WEnvironment& env) :
        Wt::WApplication(env),
        session_() {
    session_.login().changed().connect(this, &AuthApplication::authEvent);
    createLoginScreen();
}

// Creates the Login-Screen.
void AuthApplication::createLoginScreen() {
    root()->addStyleClass("container");
    auto theme = std::make_shared<Wt::WBootstrapTheme>();
    theme->setVersion(Wt::BootstrapVersion::v2);
    wApp->setTheme(theme);
    //this->setTheme(new Wt::WBootstrapTheme());
    //WApplication::requireJQuery();//, solves the jQuery version mismatch bug at my personal machine.
    requireJQuery("resources/jquery.min.js");
    //This need not be checked in.
    std::unique_ptr<Wt::WContainerWidget> container_logo = std::make_unique<Wt::WContainerWidget>();
    //Wt::WContainerWidget *container_logo = new Wt::WContainerWidget();
    Wt::WImage *image = container_logo->addWidget(std::make_unique<Wt::WImage>(Wt::WLink("icons/wt_powered.jpg")));
    image->setAlternateText("Fraunhofer ITWM");
    std::unique_ptr<Wt::Auth::AuthWidget> authWidget = std::make_unique<Wt::Auth::AuthWidget>(Session::auth(), session_.users(), session_.login());

    authWidget->model()->addPasswordAuth(&Session::passwordAuth());
    //authWidget->model()->addOAuth(Session::oAuth());
    authWidget->setRegistrationEnabled(true);

    authWidget->processEnvironment();
    root()->addWidget(std::move(container_logo));
    //Wt::WContainerWidget *container_logo = root()->addWidget(std::make_unique<Wt::WContainerWidget>());   //works fine but meh tomorrow

    root()->addWidget(std::move(authWidget));
}

// Handles user-logins and user-logouts.
void AuthApplication::authEvent() {

    //std::cout<<"---------INSIDE the authEvent() function block------------\n"; // -> von Rajveer; auskommentiert von Stefan
    if (session_.login().loggedIn()) {
        const Wt::Auth::User& u = session_.login().user();
        Wt::log("notice")
            << "User " << u.id()
            << " (" << u.identity(Wt::Auth::Identity::LoginName) << ")"
            << " logged in.";
        //populating the User table with details of the newly created user  // "newly created"?
        session_.addNames();
        createApplicationTree();
    } else {
        Wt::log("notice") << "User logged out.";
        //createApplicationTree();
        root()->clear();
        createLoginScreen();
    }
}

void AuthApplication::createApplicationTree() {

    if (appRoot().empty()) {
        std::cerr << "!!!!!!!!!!" << std::endl
                  << "!! Warning: read the README.md file for hints on deployment,"
                  << " the approot looks suspect!" << std::endl
                  << "!!!!!!!!!!" << std::endl;
    }
    //root()->removeStyleClass("container");
    // root()->setTheme(new Wt::WBootstrapTheme(app));

    // load text bundles (for the tr() function)
    messageResourceBundle().use(appRoot() + "text");
    // app->messageResourceBundle().use(app->appRoot() + "src");

    //wt 3: Wt::WHBoxLayout *layout = new Wt::WHBoxLayout(root());
    auto layout = root()->setLayout(std::make_unique<Wt::WHBoxLayout>());

    //app->root()->addWidget(std::make_unique<Wt::WText>("Your name, please? "));
    return;
#if 0
    layout->setContentsMargins(0, 0, 0, 0);
    auto baseScreen = std::make_unique<BaseScreen>(session_);
    // std::cout << "maybe add widget base screen is meh " << std::endl;
    layout->addWidget(std::move(baseScreen));
    //wt3 : layout->addWidget(new BaseScreen(session_));
    setTitle("FPMWeb");
    // std::cout << "Pre CSS stuff " << std::endl;
    useStyleSheet("style/everywidget.css");
    useStyleSheet("style/dragdrop.css");
    // std::cout << "Post CSS stuff " << std::endl;
#endif
}



// +----------------------+
// | Actual Main-Function |
// +----------------------+

std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment& env) {
        return std::make_unique<AuthApplication>(env);
}

int main(int argc, char **argv) {
    try {
        Wt::WServer server(argc, argv, WTHTTP_CONFIGURATION);
        server.addEntryPoint(Wt::EntryPointType::Application, createApplication);
        Session::configureAuth();
        std::cout << " server.run() " << std::endl;
        server.run();
        std::cout << " server running " << std::endl;  //from here on just shut down handling
        Wt::WApplication *app = Wt::WApplication::instance();
        std::cout<<"SERVER LOG "<<std::endl;
        app->internalPathChanged().connect(std::bind([=] () {
            createApplication(app->environment());
        }));
    } catch (Wt::WServer::Exception& e) {
        std::cerr << "main.C::main::WServer-Exception: " << e.what() << std::endl;
    } catch (std::exception &e) {
        std::cerr << "main.C::main::Exception: " << e.what() << std::endl;
    }
}

RE: WT3 to WT4 Port - Added by lm at almost 5 years ago

Great sleuthing! Sorry I couldn't be more help to you.

Just to be clear, you have an indictment against a particular line of code, but you still have no solution, right?

I don't use WLayouts but do everything through CSS. Flexbox and grid do amazing things.

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

I went through the code step by step ignoring all sub functions before that line which arent mandatory so I could narrow it down to the one line that breaks it and all the lines before which could cause it (the main i copied).

The moment i run

 auto layout = root()->setLayout(std::make_unique<Wt::WHBoxLayout>());  

which is literally copied from the tutorial, causes an "Wt internal error; code: undefined, description: a is null" and the javascript function part which includes a is copy and pasted above.

I am used to writing my own css but since my task was to port an existing version I didnt want to change anything. And theoretically it has to work since if I reuse the SAME code for the auth widget window it works and properly sets up a layout.

Thats why im super confused - might be a bug, might be me being terrible at c.

RE: WT3 to WT4 Port - Added by lm at almost 5 years ago

Just for laughs, what happens if you remove the setLayout call?

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

doesnt crash since there is no more nullptr created but obviously all the formatting which was done based on the layout is gone. Even menus wont show up as the background "layout" is for some reason dominant

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

could anyone help me out on this one? I copy and pasted the minimal code to get the error. It is still the line setting the layout for root(). Afaik root is a containerwidget and should therefore have this method.

What bothers me is that if I create the layout in the createLoginScreen() method I do not get this error.

Could any1 please help me - I am completly done with the rest of the code everything works but I cannot get it to display properly. Everytime I update an element inside some container and I wanna refresh the page I used to make a new layout and then call the function to fill it. Now it gives me a null element everytime I create a layout.

 #include <Wt/WString.h>           // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WString.html
#include <Wt/WApplication.h>        // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WApplication.html
#include <Wt/WEnvironment.h>        // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WEnvironment.html
#include <Wt/WBootstrapTheme.h>     // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WBootstrapTheme.html
#include <Wt/WCssTheme.h>           // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WCssTheme.html
#include <Wt/WContainerWidget.h>        // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WContainerWidget.html
#include <Wt/WHBoxLayout.h>     // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WHBoxLayout.html
#include <Wt/WImage.h>          // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WImage.html
// - - Web Application Server
#include <Wt/WServer.h>         // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WServer.html
// - - Database
#include <Wt/Dbo/Dbo.h>         // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Dbo_1_1Dbo.html
// - - Authentication
#include <Wt/Auth/AuthModel.h>      // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Auth_1_1AuthModel.html
#include <Wt/Auth/AuthWidget.h>     // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Auth_1_1AuthWidget.html
#include <Wt/Auth/PasswordService.h>    // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1Auth_1_1PasswordService.html
#include "main.h"           // Prototype definition of class "AuthApplication" and its functions.
#include "model/Session.h"      // 
#include "BaseScreen.h"         



// +--------------------------------+
// | Class-Function-Implementations |
// +--------------------------------+

AuthApplication::AuthApplication(const Wt::WEnvironment& env) :
        Wt::WApplication(env),
        session_() {
    session_.login().changed().connect(this, &AuthApplication::authEvent);
    createLoginScreen();
}

void AuthApplication::createLoginScreen() {
    auto theme = std::make_shared<Wt::WBootstrapTheme>();
    theme->setVersion(Wt::BootstrapVersion::v2);
    wApp->setTheme(theme);
    auto authWidget = root()->addWidget(std::make_unique<Wt::Auth::AuthWidget>(Session::auth(), session_.users(), session_.login()));
    authWidget->model()->addPasswordAuth(&Session::passwordAuth());
    authWidget->setRegistrationEnabled(true);
    authWidget->processEnvironment();

}
void AuthApplication::authEvent() {
        if (session_.login().loggedIn()) {
                const Wt::Auth::User& u = session_.login().user();
                createApplicationTree();
        }
}

void AuthApplication::createApplicationTree() {
    auto layout = root()->setLayout(std::make_unique<Wt::WVBoxLayout>());
}


std::unique_ptr<Wt::WApplication> createApplication(const Wt::WEnvironment& env) {
        return std::make_unique<AuthApplication>(env);
}

int main(int argc, char **argv) {
    try {
            Wt::WServer server(argc, argv, WTHTTP_CONFIGURATION);

            server.addEntryPoint(Wt::EntryPointType::Application, createApplication);

            Session::configureAuth();

            server.run();

            Wt::WApplication *app = Wt::WApplication::instance();

            app->internalPathChanged().connect(std::bind([=] () {
                createApplication(app->environment());
            }));
    } catch (Wt::WServer::Exception& e) {
        std::cerr << "main.C::main::WServer-Exception: " << e.what() << std::endl;
    } catch (std::exception &e) {
        std::cerr << "main.C::main::Exception: " << e.what() << std::endl;
    }
}

RE: WT3 to WT4 Port - Added by lm at almost 5 years ago

I wasn't able to get your minimum example up and running because it doesn't include everything. There are three includes I don't have: "main.h" "model/Session.h" "BaseScreen.h". When I get rid of those, I don't have AuthApplication defined. Looks like you're using a custom Session object which you didn't paste, too.

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

Yeah as it connects to a local data base which youd have to recreate - the code which used basescreen was removed but I didnt comment the include out. My bad.

 #ifndef MAIN_H_
#define MAIN_H_



// +------------+
// | Inclusions |
// +------------+

// - Wt-Library-Inclusions
#include <Wt/WApplication.h>        // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WApplication.html
#include <Wt/WEnvironment.h>        // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WEnvironment.html
#include <Wt/WServer.h>         // https://www.webtoolkit.eu/wt/doc/reference/html/classWt_1_1WServer.html
#include "model/Session.h"



// +--------------------------------+
// | Class- and Function-Prototypes |
// +--------------------------------+

class AuthApplication : public Wt::WApplication {

    private:
    Session session_;

    public:
    AuthApplication(const Wt::WEnvironment& env);
    void createLoginScreen();
    void authEvent();
    void createApplicationTree();

};

#endif   

I attached both Session files but since it requires a DB it would take you some time to set it up. It doenst change anthing about the appearance / visual part though. Only the main.C code does and its sufficient to create the error. I hoped someone could see my mistake by just looking at the code.

RE: WT3 to WT4 Port - Added by Sebastian Fett almost 5 years ago

Would you have a working example of a layout that gets added to overwrite a preexisting layout?

I wanna clear the complete existence of a container widget but somehow calling the ->clear() causes a seg fault.

WT 3.3.5 allowed me to call setlayout on a container after it had contents and it would remove all the previous contents and let me add new ones.

Doing this in JS/html would be so much easier :(

RE: WT3 to WT4 Port - Added by Roel Standaert almost 5 years ago

I managed to hack up the code well enough so I could get a test case. One thing I did think was kind of weird about your example is the main method, getting WApplication::instance() after WServer::run(). That will probably just crash your program when it exits instead of doing so cleanly. See issue #7128 for more info on the proposed fix.

RE: WT3 to WT4 Port - Added by lm at almost 5 years ago

I made an effort in that direction, but never quite got there. I take it you can't reproduce the issue: setting the layout causes the strange error?

RE: WT3 to WT4 Port - Added by Roel Standaert almost 5 years ago

I could reproduce it. I think it could actually be considered a bug in Wt 4. Fixing it entails changing the interface of AuthWidget a bit though.

    (1-19/19)